blob: 7760d72cc08877a44d53be5f5643e5eaded8f1a2 [file] [log] [blame]
/*
* Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "config.h"
#include "SerializedScriptValue.h"
#include "Blob.h"
#include "CryptoKeyAES.h"
#include "CryptoKeyDataOctetSequence.h"
#include "CryptoKeyDataRSAComponents.h"
#include "CryptoKeyHMAC.h"
#include "CryptoKeyRSA.h"
#include "ExceptionCode.h"
#include "File.h"
#include "FileList.h"
#include "ImageData.h"
#include "JSBlob.h"
#include "JSCryptoKey.h"
#include "JSDOMGlobalObject.h"
#include "JSFile.h"
#include "JSFileList.h"
#include "JSImageData.h"
#include "JSMessagePort.h"
#include "JSNavigator.h"
#include "NotImplemented.h"
#include "SharedBuffer.h"
#include "WebCoreJSClientData.h"
#include <limits>
#include <JavaScriptCore/APICast.h>
#include <JavaScriptCore/APIShims.h>
#include <runtime/ArrayBuffer.h>
#include <runtime/BooleanObject.h>
#include <runtime/DateInstance.h>
#include <runtime/Error.h>
#include <runtime/ExceptionHelpers.h>
#include <runtime/JSArrayBuffer.h>
#include <runtime/JSArrayBufferView.h>
#include <runtime/JSCInlines.h>
#include <runtime/JSDataView.h>
#include <runtime/JSMap.h>
#include <runtime/JSSet.h>
#include <runtime/JSTypedArrays.h>
#include <runtime/MapData.h>
#include <runtime/ObjectConstructor.h>
#include <runtime/PropertyNameArray.h>
#include <runtime/RegExp.h>
#include <runtime/RegExpObject.h>
#include <runtime/TypedArrayInlines.h>
#include <runtime/TypedArrays.h>
#include <wtf/HashTraits.h>
#include <wtf/Vector.h>
using namespace JSC;
#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS)
#define ASSUME_LITTLE_ENDIAN 0
#else
#define ASSUME_LITTLE_ENDIAN 1
#endif
namespace WebCore {
static const unsigned maximumFilterRecursion = 40000;
enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember,
ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember,
MapDataStartVisitEntry, MapDataEndVisitKey, MapDataEndVisitValue };
// These can't be reordered, and any new types must be added to the end of the list
enum SerializationTag {
ArrayTag = 1,
ObjectTag = 2,
UndefinedTag = 3,
NullTag = 4,
IntTag = 5,
ZeroTag = 6,
OneTag = 7,
FalseTag = 8,
TrueTag = 9,
DoubleTag = 10,
DateTag = 11,
FileTag = 12,
FileListTag = 13,
ImageDataTag = 14,
BlobTag = 15,
StringTag = 16,
EmptyStringTag = 17,
RegExpTag = 18,
ObjectReferenceTag = 19,
MessagePortReferenceTag = 20,
ArrayBufferTag = 21,
ArrayBufferViewTag = 22,
ArrayBufferTransferTag = 23,
TrueObjectTag = 24,
FalseObjectTag = 25,
StringObjectTag = 26,
EmptyStringObjectTag = 27,
NumberObjectTag = 28,
SetObjectTag = 29,
MapObjectTag = 30,
NonMapPropertiesTag = 31,
#if ENABLE(SUBTLE_CRYPTO)
CryptoKeyTag = 32,
#endif
ErrorTag = 255
};
enum ArrayBufferViewSubtag {
DataViewTag = 0,
Int8ArrayTag = 1,
Uint8ArrayTag = 2,
Uint8ClampedArrayTag = 3,
Int16ArrayTag = 4,
Uint16ArrayTag = 5,
Int32ArrayTag = 6,
Uint32ArrayTag = 7,
Float32ArrayTag = 8,
Float64ArrayTag = 9
};
static unsigned typedArrayElementSize(ArrayBufferViewSubtag tag)
{
switch (tag) {
case DataViewTag:
case Int8ArrayTag:
case Uint8ArrayTag:
case Uint8ClampedArrayTag:
return 1;
case Int16ArrayTag:
case Uint16ArrayTag:
return 2;
case Int32ArrayTag:
case Uint32ArrayTag:
case Float32ArrayTag:
return 4;
case Float64ArrayTag:
return 8;
default:
return 0;
}
}
#if ENABLE(SUBTLE_CRYPTO)
enum class CryptoKeyClassSubtag {
HMAC = 0,
AES = 1,
RSA = 2
};
const uint8_t cryptoKeyClassSubtagMaximumValue = 2;
enum class CryptoKeyAsymmetricTypeSubtag {
Public = 0,
Private = 1
};
const uint8_t cryptoKeyAsymmetricTypeSubtagMaximumValue = 1;
enum class CryptoKeyUsageTag {
Encrypt = 0,
Decrypt = 1,
Sign = 2,
Verify = 3,
DeriveKey = 4,
DeriveBits = 5,
WrapKey = 6,
UnwrapKey = 7
};
const uint8_t cryptoKeyUsageTagMaximumValue = 7;
enum class CryptoAlgorithmIdentifierTag {
RSAES_PKCS1_v1_5 = 0,
RSASSA_PKCS1_v1_5 = 1,
RSA_PSS = 2,
RSA_OAEP = 3,
ECDSA = 4,
ECDH = 5,
AES_CTR = 6,
AES_CBC = 7,
AES_CMAC = 8,
AES_GCM = 9,
AES_CFB = 10,
AES_KW = 11,
HMAC = 12,
DH = 13,
SHA_1 = 14,
SHA_224 = 15,
SHA_256 = 16,
SHA_384 = 17,
SHA_512 = 18,
CONCAT = 19,
HKDF_CTR = 20,
PBKDF2 = 21,
};
const uint8_t cryptoAlgorithmIdentifierTagMaximumValue = 21;
static unsigned countUsages(CryptoKeyUsage usages)
{
// Fast bit count algorithm for sparse bit maps.
unsigned count = 0;
while (usages) {
usages = usages & (usages - 1);
++count;
}
return count;
}
#endif
/* CurrentVersion tracks the serialization version so that persistent stores
* are able to correctly bail out in the case of encountering newer formats.
*
* Initial version was 1.
* Version 2. added the ObjectReferenceTag and support for serialization of cyclic graphs.
* Version 3. added the FalseObjectTag, TrueObjectTag, NumberObjectTag, StringObjectTag
* and EmptyStringObjectTag for serialization of Boolean, Number and String objects.
* Version 4. added support for serializing non-index properties of arrays.
* Version 5. added support for Map and Set types.
*/
static const unsigned CurrentVersion = 5;
static const unsigned TerminatorTag = 0xFFFFFFFF;
static const unsigned StringPoolTag = 0xFFFFFFFE;
static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD;
/*
* Object serialization is performed according to the following grammar, all tags
* are recorded as a single uint8_t.
*
* IndexType (used for the object pool and StringData's constant pool) is the
* minimum sized unsigned integer type required to represent the maximum index
* in the constant pool.
*
* SerializedValue :- <CurrentVersion:uint32_t> Value
* Value :- Array | Object | Map | Set | Terminal
*
* Array :-
* ArrayTag <length:uint32_t>(<index:uint32_t><value:Value>)* TerminatorTag
*
* Object :-
* ObjectTag (<name:StringData><value:Value>)* TerminatorTag
*
* Map :- MapObjectTag MapData
*
* Set :- SetObjectTag MapData
*
* MapData :- (<key:Value><value:Value>) NonMapPropertiesTag (<name:StringData><value:Value>)* TerminatorTag
*
* Terminal :-
* UndefinedTag
* | NullTag
* | IntTag <value:int32_t>
* | ZeroTag
* | OneTag
* | FalseTag
* | TrueTag
* | FalseObjectTag
* | TrueObjectTag
* | DoubleTag <value:double>
* | NumberObjectTag <value:double>
* | DateTag <value:double>
* | String
* | EmptyStringTag
* | EmptyStringObjectTag
* | File
* | FileList
* | ImageData
* | Blob
* | ObjectReference
* | MessagePortReferenceTag <value:uint32_t>
* | ArrayBuffer
* | ArrayBufferViewTag ArrayBufferViewSubtag <byteOffset:uint32_t> <byteLength:uint32_t> (ArrayBuffer | ObjectReference)
* | ArrayBufferTransferTag <value:uint32_t>
* | CryptoKeyTag <extractable:int32_t> <usagesCount:uint32_t> <usages:byte{usagesCount}> CryptoKeyClassSubtag (CryptoKeyHMAC | CryptoKeyAES | CryptoKeyRSA)
*
* String :-
* EmptyStringTag
* StringTag StringData
*
* StringObject:
* EmptyStringObjectTag
* StringObjectTag StringData
*
* StringData :-
* StringPoolTag <cpIndex:IndexType>
* (not (TerminatorTag | StringPoolTag))<length:uint32_t><characters:UChar{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed
*
* File :-
* FileTag FileData
*
* FileData :-
* <path:StringData> <url:StringData> <type:StringData>
*
* FileList :-
* FileListTag <length:uint32_t>(<file:FileData>){length}
*
* ImageData :-
* ImageDataTag <width:int32_t><height:int32_t><length:uint32_t><data:uint8_t{length}>
*
* Blob :-
* BlobTag <url:StringData><type:StringData><size:long long>
*
* RegExp :-
* RegExpTag <pattern:StringData><flags:StringData>
*
* ObjectReference :-
* ObjectReferenceTag <opIndex:IndexType>
*
* ArrayBuffer :-
* ArrayBufferTag <length:uint32_t> <contents:byte{length}>
*
* CryptoKeyHMAC :-
* <keySize:uint32_t> <keyData:byte{keySize}> CryptoAlgorithmIdentifierTag // Algorithm tag inner hash function.
*
* CryptoKeyAES :-
* CryptoAlgorithmIdentifierTag <keySize:uint32_t> <keyData:byte{keySize}>
*
* CryptoKeyRSA :-
* CryptoAlgorithmIdentifierTag <isRestrictedToHash:int32_t> CryptoAlgorithmIdentifierTag? CryptoKeyAsymmetricTypeSubtag CryptoKeyRSAPublicComponents CryptoKeyRSAPrivateComponents?
*
* CryptoKeyRSAPublicComponents :-
* <modulusSize:uint32_t> <modulus:byte{modulusSize}> <exponentSize:uint32_t> <exponent:byte{exponentSize}>
*
* CryptoKeyRSAPrivateComponents :-
* <privateExponentSize:uint32_t> <privateExponent:byte{privateExponentSize}> <primeCount:uint32_t> FirstPrimeInfo? PrimeInfo{primeCount - 1}
*
* // CRT data could be computed from prime factors. It is only serialized to reuse a code path that's needed for JWK.
* FirstPrimeInfo :-
* <factorSize:uint32_t> <factor:byte{factorSize}> <crtExponentSize:uint32_t> <crtExponent:byte{crtExponentSize}>
*
* PrimeInfo :-
* <factorSize:uint32_t> <factor:byte{factorSize}> <crtExponentSize:uint32_t> <crtExponent:byte{crtExponentSize}> <crtCoefficientSize:uint32_t> <crtCoefficient:byte{crtCoefficientSize}>
*/
typedef std::pair<JSC::JSValue, SerializationReturnCode> DeserializationResult;
class CloneBase {
protected:
CloneBase(ExecState* exec)
: m_exec(exec)
, m_failed(false)
{
}
bool shouldTerminate()
{
return m_exec->hadException();
}
void throwStackOverflow()
{
m_exec->vm().throwException(m_exec, createStackOverflowError(m_exec));
}
NO_RETURN_DUE_TO_ASSERT
void fail()
{
ASSERT_NOT_REACHED();
m_failed = true;
}
ExecState* m_exec;
bool m_failed;
MarkedArgumentBuffer m_gcBuffer;
};
#if ASSUME_LITTLE_ENDIAN
template <typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value)
{
buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value));
}
#else
template <typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value)
{
for (unsigned i = 0; i < sizeof(T); i++) {
buffer.append(value & 0xFF);
value >>= 8;
}
}
#endif
template <> void writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, uint8_t value)
{
buffer.append(value);
}
template <typename T> static bool writeLittleEndian(Vector<uint8_t>& buffer, const T* values, uint32_t length)
{
if (length > std::numeric_limits<uint32_t>::max() / sizeof(T))
return false;
#if ASSUME_LITTLE_ENDIAN
buffer.append(reinterpret_cast<const uint8_t*>(values), length * sizeof(T));
#else
for (unsigned i = 0; i < length; i++) {
T value = values[i];
for (unsigned j = 0; j < sizeof(T); j++) {
buffer.append(static_cast<uint8_t>(value & 0xFF));
value >>= 8;
}
}
#endif
return true;
}
template <> bool writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, const uint8_t* values, uint32_t length)
{
buffer.append(values, length);
return true;
}
class CloneSerializer : CloneBase {
public:
static SerializationReturnCode serialize(ExecState* exec, JSValue value,
MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers,
Vector<String>& blobURLs, Vector<uint8_t>& out)
{
CloneSerializer serializer(exec, messagePorts, arrayBuffers, blobURLs, out);
return serializer.serialize(value);
}
static bool serialize(const String& s, Vector<uint8_t>& out)
{
writeLittleEndian(out, CurrentVersion);
if (s.isEmpty()) {
writeLittleEndian<uint8_t>(out, EmptyStringTag);
return true;
}
writeLittleEndian<uint8_t>(out, StringTag);
writeLittleEndian(out, s.length());
return writeLittleEndian(out, s.impl()->deprecatedCharacters(), s.length());
}
static void serializeUndefined(Vector<uint8_t>& out)
{
writeLittleEndian(out, CurrentVersion);
writeLittleEndian<uint8_t>(out, UndefinedTag);
}
static void serializeBoolean(bool value, Vector<uint8_t>& out)
{
writeLittleEndian(out, CurrentVersion);
writeLittleEndian<uint8_t>(out, value ? TrueTag : FalseTag);
}
static void serializeNumber(double value, Vector<uint8_t>& out)
{
writeLittleEndian(out, CurrentVersion);
writeLittleEndian<uint8_t>(out, DoubleTag);
union {
double d;
int64_t i;
} u;
u.d = value;
writeLittleEndian(out, u.i);
}
private:
typedef HashMap<JSObject*, uint32_t> ObjectPool;
CloneSerializer(ExecState* exec, MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, Vector<String>& blobURLs, Vector<uint8_t>& out)
: CloneBase(exec)
, m_buffer(out)
, m_blobURLs(blobURLs)
, m_emptyIdentifier(exec, emptyString())
{
write(CurrentVersion);
fillTransferMap(messagePorts, m_transferredMessagePorts);
fillTransferMap(arrayBuffers, m_transferredArrayBuffers);
}
template <class T>
void fillTransferMap(Vector<RefPtr<T>, 1>* input, ObjectPool& result)
{
if (!input)
return;
JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(m_exec->lexicalGlobalObject());
for (size_t i = 0; i < input->size(); i++) {
JSC::JSValue value = toJS(m_exec, globalObject, input->at(i).get());
JSC::JSObject* obj = value.getObject();
if (obj && !result.contains(obj))
result.add(obj, i);
}
}
SerializationReturnCode serialize(JSValue in);
bool isArray(JSValue value)
{
if (!value.isObject())
return false;
JSObject* object = asObject(value);
return isJSArray(object) || object->inherits(JSArray::info());
}
bool isMap(JSValue value)
{
if (!value.isObject())
return false;
JSObject* object = asObject(value);
return object->inherits(JSMap::info());
}
bool isSet(JSValue value)
{
if (!value.isObject())
return false;
JSObject* object = asObject(value);
return object->inherits(JSSet::info());
}
bool checkForDuplicate(JSObject* object)
{
// Record object for graph reconstruction
ObjectPool::const_iterator found = m_objectPool.find(object);
// Handle duplicate references
if (found != m_objectPool.end()) {
write(ObjectReferenceTag);
ASSERT(static_cast<int32_t>(found->value) < m_objectPool.size());
writeObjectIndex(found->value);
return true;
}
return false;
}
void recordObject(JSObject* object)
{
m_objectPool.add(object, m_objectPool.size());
m_gcBuffer.append(object);
}
bool startObjectInternal(JSObject* object)
{
if (checkForDuplicate(object))
return false;
recordObject(object);
return true;
}
bool startObject(JSObject* object)
{
if (!startObjectInternal(object))
return false;
write(ObjectTag);
return true;
}
bool startArray(JSArray* array)
{
if (!startObjectInternal(array))
return false;
unsigned length = array->length();
write(ArrayTag);
write(length);
return true;
}
bool startSet(JSSet* set)
{
if (!startObjectInternal(set))
return false;
write(SetObjectTag);
return true;
}
bool startMap(JSMap* map)
{
if (!startObjectInternal(map))
return false;
write(MapObjectTag);
return true;
}
void endObject()
{
write(TerminatorTag);
}
JSValue getProperty(JSObject* object, const Identifier& propertyName)
{
PropertySlot slot(object);
if (object->methodTable()->getOwnPropertySlot(object, m_exec, propertyName, slot))
return slot.getValue(m_exec, propertyName);
return JSValue();
}
void dumpImmediate(JSValue value)
{
if (value.isNull())
write(NullTag);
else if (value.isUndefined())
write(UndefinedTag);
else if (value.isNumber()) {
if (value.isInt32()) {
if (!value.asInt32())
write(ZeroTag);
else if (value.asInt32() == 1)
write(OneTag);
else {
write(IntTag);
write(static_cast<uint32_t>(value.asInt32()));
}
} else {
write(DoubleTag);
write(value.asDouble());
}
} else if (value.isBoolean()) {
if (value.isTrue())
write(TrueTag);
else
write(FalseTag);
}
}
void dumpString(String str)
{
if (str.isEmpty())
write(EmptyStringTag);
else {
write(StringTag);
write(str);
}
}
void dumpStringObject(String str)
{
if (str.isEmpty())
write(EmptyStringObjectTag);
else {
write(StringObjectTag);
write(str);
}
}
bool dumpArrayBufferView(JSObject* obj, SerializationReturnCode& code)
{
write(ArrayBufferViewTag);
if (obj->inherits(JSDataView::info()))
write(DataViewTag);
else if (obj->inherits(JSUint8ClampedArray::info()))
write(Uint8ClampedArrayTag);
else if (obj->inherits(JSInt8Array::info()))
write(Int8ArrayTag);
else if (obj->inherits(JSUint8Array::info()))
write(Uint8ArrayTag);
else if (obj->inherits(JSInt16Array::info()))
write(Int16ArrayTag);
else if (obj->inherits(JSUint16Array::info()))
write(Uint16ArrayTag);
else if (obj->inherits(JSInt32Array::info()))
write(Int32ArrayTag);
else if (obj->inherits(JSUint32Array::info()))
write(Uint32ArrayTag);
else if (obj->inherits(JSFloat32Array::info()))
write(Float32ArrayTag);
else if (obj->inherits(JSFloat64Array::info()))
write(Float64ArrayTag);
else
return false;
RefPtr<ArrayBufferView> arrayBufferView = toArrayBufferView(obj);
write(static_cast<uint32_t>(arrayBufferView->byteOffset()));
write(static_cast<uint32_t>(arrayBufferView->byteLength()));
RefPtr<ArrayBuffer> arrayBuffer = arrayBufferView->buffer();
if (!arrayBuffer) {
code = ValidationError;
return true;
}
JSValue bufferObj = toJS(m_exec, jsCast<JSDOMGlobalObject*>(m_exec->lexicalGlobalObject()), arrayBuffer.get());
return dumpIfTerminal(bufferObj, code);
}
bool dumpIfTerminal(JSValue value, SerializationReturnCode& code)
{
if (!value.isCell()) {
dumpImmediate(value);
return true;
}
if (value.isString()) {
String str = asString(value)->value(m_exec);
dumpString(str);
return true;
}
if (value.isNumber()) {
write(DoubleTag);
write(value.asNumber());
return true;
}
if (value.isObject() && asObject(value)->inherits(DateInstance::info())) {
write(DateTag);
write(asDateInstance(value)->internalNumber());
return true;
}
if (isArray(value))
return false;
if (value.isObject()) {
JSObject* obj = asObject(value);
if (obj->inherits(BooleanObject::info())) {
if (!startObjectInternal(obj)) // handle duplicates
return true;
write(asBooleanObject(value)->internalValue().toBoolean(m_exec) ? TrueObjectTag : FalseObjectTag);
return true;
}
if (obj->inherits(StringObject::info())) {
if (!startObjectInternal(obj)) // handle duplicates
return true;
String str = asString(asStringObject(value)->internalValue())->value(m_exec);
dumpStringObject(str);
return true;
}
if (obj->inherits(NumberObject::info())) {
if (!startObjectInternal(obj)) // handle duplicates
return true;
write(NumberObjectTag);
NumberObject* obj = static_cast<NumberObject*>(asObject(value));
write(obj->internalValue().asNumber());
return true;
}
if (File* file = toFile(obj)) {
write(FileTag);
write(file);
return true;
}
if (FileList* list = toFileList(obj)) {
write(FileListTag);
unsigned length = list->length();
write(length);
for (unsigned i = 0; i < length; i++)
write(list->item(i));
return true;
}
if (Blob* blob = toBlob(obj)) {
write(BlobTag);
m_blobURLs.append(blob->url());
write(blob->url());
write(blob->type());
write(blob->size());
return true;
}
if (ImageData* data = toImageData(obj)) {
write(ImageDataTag);
write(data->width());
write(data->height());
write(data->data()->length());
write(data->data()->data(), data->data()->length());
return true;
}
if (obj->inherits(RegExpObject::info())) {
RegExpObject* regExp = asRegExpObject(obj);
char flags[3];
int flagCount = 0;
if (regExp->regExp()->global())
flags[flagCount++] = 'g';
if (regExp->regExp()->ignoreCase())
flags[flagCount++] = 'i';
if (regExp->regExp()->multiline())
flags[flagCount++] = 'm';
write(RegExpTag);
write(regExp->regExp()->pattern());
write(String(flags, flagCount));
return true;
}
if (obj->inherits(JSMessagePort::info())) {
ObjectPool::iterator index = m_transferredMessagePorts.find(obj);
if (index != m_transferredMessagePorts.end()) {
write(MessagePortReferenceTag);
write(index->value);
return true;
}
// MessagePort object could not be found in transferred message ports
code = ValidationError;
return true;
}
if (ArrayBuffer* arrayBuffer = toArrayBuffer(obj)) {
if (arrayBuffer->isNeutered()) {
code = ValidationError;
return true;
}
ObjectPool::iterator index = m_transferredArrayBuffers.find(obj);
if (index != m_transferredArrayBuffers.end()) {
write(ArrayBufferTransferTag);
write(index->value);
return true;
}
if (!startObjectInternal(obj)) // handle duplicates
return true;
write(ArrayBufferTag);
write(arrayBuffer->byteLength());
write(static_cast<const uint8_t*>(arrayBuffer->data()), arrayBuffer->byteLength());
return true;
}
if (obj->inherits(JSArrayBufferView::info())) {
if (checkForDuplicate(obj))
return true;
bool success = dumpArrayBufferView(obj, code);
recordObject(obj);
return success;
}
#if ENABLE(SUBTLE_CRYPTO)
if (CryptoKey* key = toCryptoKey(obj)) {
write(CryptoKeyTag);
write(key);
return true;
}
#endif
return false;
}
// Any other types are expected to serialize as null.
write(NullTag);
return true;
}
void write(SerializationTag tag)
{
writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
}
void write(ArrayBufferViewSubtag tag)
{
writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
}
#if ENABLE(SUBTLE_CRYPTO)
void write(CryptoKeyClassSubtag tag)
{
writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
}
void write(CryptoKeyAsymmetricTypeSubtag tag)
{
writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
}
void write(CryptoKeyUsageTag tag)
{
writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
}
void write(CryptoAlgorithmIdentifierTag tag)
{
writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
}
#endif
void write(uint8_t c)
{
writeLittleEndian(m_buffer, c);
}
void write(uint32_t i)
{
writeLittleEndian(m_buffer, i);
}
void write(double d)
{
union {
double d;
int64_t i;
} u;
u.d = d;
writeLittleEndian(m_buffer, u.i);
}
void write(int32_t i)
{
writeLittleEndian(m_buffer, i);
}
void write(unsigned long long i)
{
writeLittleEndian(m_buffer, i);
}
void write(uint16_t ch)
{
writeLittleEndian(m_buffer, ch);
}
void writeStringIndex(unsigned i)
{
writeConstantPoolIndex(m_constantPool, i);
}
void writeObjectIndex(unsigned i)
{
writeConstantPoolIndex(m_objectPool, i);
}
template <class T> void writeConstantPoolIndex(const T& constantPool, unsigned i)
{
ASSERT(static_cast<int32_t>(i) < constantPool.size());
if (constantPool.size() <= 0xFF)
write(static_cast<uint8_t>(i));
else if (constantPool.size() <= 0xFFFF)
write(static_cast<uint16_t>(i));
else
write(static_cast<uint32_t>(i));
}
void write(const Identifier& ident)
{
const String& str = ident.string();
StringConstantPool::AddResult addResult = m_constantPool.add(str.impl(), m_constantPool.size());
if (!addResult.isNewEntry) {
write(StringPoolTag);
writeStringIndex(addResult.iterator->value);
return;
}
// This condition is unlikely to happen as they would imply an ~8gb
// string but we should guard against it anyway
if (str.length() >= StringPoolTag) {
fail();
return;
}
// Guard against overflow
if (str.length() > (std::numeric_limits<uint32_t>::max() - sizeof(uint32_t)) / sizeof(UChar)) {
fail();
return;
}
writeLittleEndian<uint32_t>(m_buffer, str.length());
if (!writeLittleEndian<uint16_t>(m_buffer, reinterpret_cast<const uint16_t*>(str.deprecatedCharacters()), str.length()))
fail();
}
void write(const String& str)
{
if (str.isNull())
write(m_emptyIdentifier);
else
write(Identifier(m_exec, str));
}
void write(const Vector<uint8_t>& vector)
{
uint32_t size = vector.size();
write(size);
writeLittleEndian(m_buffer, vector.data(), size);
}
void write(const File* file)
{
m_blobURLs.append(file->url());
write(file->path());
write(file->url());
write(file->type());
}
#if ENABLE(SUBTLE_CRYPTO)
void write(CryptoAlgorithmIdentifier algorithm)
{
switch (algorithm) {
case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
write(CryptoAlgorithmIdentifierTag::RSAES_PKCS1_v1_5);
break;
case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
write(CryptoAlgorithmIdentifierTag::RSASSA_PKCS1_v1_5);
break;
case CryptoAlgorithmIdentifier::RSA_PSS:
write(CryptoAlgorithmIdentifierTag::RSA_PSS);
break;
case CryptoAlgorithmIdentifier::RSA_OAEP:
write(CryptoAlgorithmIdentifierTag::RSA_OAEP);
break;
case CryptoAlgorithmIdentifier::ECDSA:
write(CryptoAlgorithmIdentifierTag::ECDSA);
break;
case CryptoAlgorithmIdentifier::ECDH:
write(CryptoAlgorithmIdentifierTag::ECDH);
break;
case CryptoAlgorithmIdentifier::AES_CTR:
write(CryptoAlgorithmIdentifierTag::AES_CTR);
break;
case CryptoAlgorithmIdentifier::AES_CBC:
write(CryptoAlgorithmIdentifierTag::AES_CBC);
break;
case CryptoAlgorithmIdentifier::AES_CMAC:
write(CryptoAlgorithmIdentifierTag::AES_CMAC);
break;
case CryptoAlgorithmIdentifier::AES_GCM:
write(CryptoAlgorithmIdentifierTag::AES_GCM);
break;
case CryptoAlgorithmIdentifier::AES_CFB:
write(CryptoAlgorithmIdentifierTag::AES_CFB);
break;
case CryptoAlgorithmIdentifier::AES_KW:
write(CryptoAlgorithmIdentifierTag::AES_KW);
break;
case CryptoAlgorithmIdentifier::HMAC:
write(CryptoAlgorithmIdentifierTag::HMAC);
break;
case CryptoAlgorithmIdentifier::DH:
write(CryptoAlgorithmIdentifierTag::DH);
break;
case CryptoAlgorithmIdentifier::SHA_1:
write(CryptoAlgorithmIdentifierTag::SHA_1);
break;
case CryptoAlgorithmIdentifier::SHA_224:
write(CryptoAlgorithmIdentifierTag::SHA_224);
break;
case CryptoAlgorithmIdentifier::SHA_256:
write(CryptoAlgorithmIdentifierTag::SHA_256);
break;
case CryptoAlgorithmIdentifier::SHA_384:
write(CryptoAlgorithmIdentifierTag::SHA_384);
break;
case CryptoAlgorithmIdentifier::SHA_512:
write(CryptoAlgorithmIdentifierTag::SHA_512);
break;
case CryptoAlgorithmIdentifier::CONCAT:
write(CryptoAlgorithmIdentifierTag::CONCAT);
break;
case CryptoAlgorithmIdentifier::HKDF_CTR:
write(CryptoAlgorithmIdentifierTag::HKDF_CTR);
break;
case CryptoAlgorithmIdentifier::PBKDF2:
write(CryptoAlgorithmIdentifierTag::PBKDF2);
break;
}
}
void write(CryptoKeyDataRSAComponents::Type type)
{
switch (type) {
case CryptoKeyDataRSAComponents::Type::Public:
write(CryptoKeyAsymmetricTypeSubtag::Public);
return;
case CryptoKeyDataRSAComponents::Type::Private:
write(CryptoKeyAsymmetricTypeSubtag::Private);
return;
}
}
void write(const CryptoKeyDataRSAComponents& key)
{
write(key.type());
write(key.modulus());
write(key.exponent());
if (key.type() == CryptoKeyDataRSAComponents::Type::Public)
return;
write(key.privateExponent());
unsigned primeCount = key.hasAdditionalPrivateKeyParameters() ? key.otherPrimeInfos().size() + 2 : 0;
write(primeCount);
if (!primeCount)
return;
write(key.firstPrimeInfo().primeFactor);
write(key.firstPrimeInfo().factorCRTExponent);
write(key.secondPrimeInfo().primeFactor);
write(key.secondPrimeInfo().factorCRTExponent);
write(key.secondPrimeInfo().factorCRTCoefficient);
for (unsigned i = 2; i < primeCount; ++i) {
write(key.otherPrimeInfos()[i].primeFactor);
write(key.otherPrimeInfos()[i].factorCRTExponent);
write(key.otherPrimeInfos()[i].factorCRTCoefficient);
}
}
void write(const CryptoKey* key)
{
write(key->extractable());
CryptoKeyUsage usages = key->usagesBitmap();
write(countUsages(usages));
if (usages & CryptoKeyUsageEncrypt)
write(CryptoKeyUsageTag::Encrypt);
if (usages & CryptoKeyUsageDecrypt)
write(CryptoKeyUsageTag::Decrypt);
if (usages & CryptoKeyUsageSign)
write(CryptoKeyUsageTag::Sign);
if (usages & CryptoKeyUsageVerify)
write(CryptoKeyUsageTag::Verify);
if (usages & CryptoKeyUsageDeriveKey)
write(CryptoKeyUsageTag::DeriveKey);
if (usages & CryptoKeyUsageDeriveBits)
write(CryptoKeyUsageTag::DeriveBits);
if (usages & CryptoKeyUsageWrapKey)
write(CryptoKeyUsageTag::WrapKey);
if (usages & CryptoKeyUsageUnwrapKey)
write(CryptoKeyUsageTag::UnwrapKey);
switch (key->keyClass()) {
case CryptoKeyClass::HMAC:
write(CryptoKeyClassSubtag::HMAC);
write(toCryptoKeyHMAC(key)->key());
write(toCryptoKeyHMAC(key)->hashAlgorithmIdentifier());
break;
case CryptoKeyClass::AES:
write(CryptoKeyClassSubtag::AES);
write(key->algorithmIdentifier());
write(toCryptoKeyAES(key)->key());
break;
case CryptoKeyClass::RSA:
write(CryptoKeyClassSubtag::RSA);
write(key->algorithmIdentifier());
CryptoAlgorithmIdentifier hash;
bool isRestrictedToHash = toCryptoKeyRSA(key)->isRestrictedToHash(hash);
write(isRestrictedToHash);
if (isRestrictedToHash)
write(hash);
write(toCryptoKeyDataRSAComponents(*key->exportData()));
break;
}
}
#endif
void write(const uint8_t* data, unsigned length)
{
m_buffer.append(data, length);
}
Vector<uint8_t>& m_buffer;
Vector<String>& m_blobURLs;
ObjectPool m_objectPool;
ObjectPool m_transferredMessagePorts;
ObjectPool m_transferredArrayBuffers;
typedef HashMap<RefPtr<StringImpl>, uint32_t, IdentifierRepHash> StringConstantPool;
StringConstantPool m_constantPool;
Identifier m_emptyIdentifier;
};
SerializationReturnCode CloneSerializer::serialize(JSValue in)
{
Vector<uint32_t, 16> indexStack;
Vector<uint32_t, 16> lengthStack;
Vector<PropertyNameArray, 16> propertyStack;
Vector<JSObject*, 32> inputObjectStack;
Vector<MapData*, 4> mapDataStack;
Vector<MapData::const_iterator, 4> iteratorStack;
Vector<WalkerState, 16> stateStack;
WalkerState state = StateUnknown;
JSValue inValue = in;
while (1) {
switch (state) {
arrayStartState:
case ArrayStartState: {
ASSERT(isArray(inValue));
if (inputObjectStack.size() > maximumFilterRecursion)
return StackOverflowError;
JSArray* inArray = asArray(inValue);
unsigned length = inArray->length();
if (!startArray(inArray))
break;
inputObjectStack.append(inArray);
indexStack.append(0);
lengthStack.append(length);
}
arrayStartVisitMember:
FALLTHROUGH;
case ArrayStartVisitMember: {
JSObject* array = inputObjectStack.last();
uint32_t index = indexStack.last();
if (index == lengthStack.last()) {
indexStack.removeLast();
lengthStack.removeLast();
propertyStack.append(PropertyNameArray(m_exec));
array->methodTable()->getOwnNonIndexPropertyNames(array, m_exec, propertyStack.last(), ExcludeDontEnumProperties);
if (propertyStack.last().size()) {
write(NonIndexPropertiesTag);
indexStack.append(0);
goto objectStartVisitMember;
}
propertyStack.removeLast();
endObject();
inputObjectStack.removeLast();
break;
}
inValue = array->getDirectIndex(m_exec, index);
if (!inValue) {
indexStack.last()++;
goto arrayStartVisitMember;
}
write(index);
SerializationReturnCode terminalCode = SuccessfullyCompleted;
if (dumpIfTerminal(inValue, terminalCode)) {
if (terminalCode != SuccessfullyCompleted)
return terminalCode;
indexStack.last()++;
goto arrayStartVisitMember;
}
stateStack.append(ArrayEndVisitMember);
goto stateUnknown;
}
case ArrayEndVisitMember: {
indexStack.last()++;
goto arrayStartVisitMember;
}
objectStartState:
case ObjectStartState: {
ASSERT(inValue.isObject());
if (inputObjectStack.size() > maximumFilterRecursion)
return StackOverflowError;
JSObject* inObject = asObject(inValue);
if (!startObject(inObject))
break;
// At this point, all supported objects other than Object
// objects have been handled. If we reach this point and
// the input is not an Object object then we should throw
// a DataCloneError.
if (inObject->classInfo() != JSFinalObject::info())
return DataCloneError;
inputObjectStack.append(inObject);
indexStack.append(0);
propertyStack.append(PropertyNameArray(m_exec));
inObject->methodTable()->getOwnPropertyNames(inObject, m_exec, propertyStack.last(), ExcludeDontEnumProperties);
}
objectStartVisitMember:
FALLTHROUGH;
case ObjectStartVisitMember: {
JSObject* object = inputObjectStack.last();
uint32_t index = indexStack.last();
PropertyNameArray& properties = propertyStack.last();
if (index == properties.size()) {
endObject();
inputObjectStack.removeLast();
indexStack.removeLast();
propertyStack.removeLast();
break;
}
inValue = getProperty(object, properties[index]);
if (shouldTerminate())
return ExistingExceptionError;
if (!inValue) {
// Property was removed during serialisation
indexStack.last()++;
goto objectStartVisitMember;
}
write(properties[index]);
if (shouldTerminate())
return ExistingExceptionError;
SerializationReturnCode terminalCode = SuccessfullyCompleted;
if (!dumpIfTerminal(inValue, terminalCode)) {
stateStack.append(ObjectEndVisitMember);
goto stateUnknown;
}
if (terminalCode != SuccessfullyCompleted)
return terminalCode;
FALLTHROUGH;
}
case ObjectEndVisitMember: {
if (shouldTerminate())
return ExistingExceptionError;
indexStack.last()++;
goto objectStartVisitMember;
}
mapStartState: {
ASSERT(inValue.isObject());
if (inputObjectStack.size() > maximumFilterRecursion)
return StackOverflowError;
JSMap* inMap = jsCast<JSMap*>(inValue);
if (!startMap(inMap))
break;
MapData* mapData = inMap->mapData();
m_gcBuffer.append(mapData);
mapDataStack.append(mapData);
iteratorStack.append(mapData->begin());
inputObjectStack.append(inMap);
goto mapDataStartVisitEntry;
}
setStartState: {
ASSERT(inValue.isObject());
if (inputObjectStack.size() > maximumFilterRecursion)
return StackOverflowError;
JSSet* inSet = jsCast<JSSet*>(inValue);
if (!startSet(inSet))
break;
MapData* mapData = inSet->mapData();
m_gcBuffer.append(mapData);
mapDataStack.append(mapData);
iteratorStack.append(mapData->begin());
inputObjectStack.append(inSet);
goto mapDataStartVisitEntry;
}
mapDataStartVisitEntry:
case MapDataStartVisitEntry: {
MapData::const_iterator& ptr = iteratorStack.last();
MapData* mapData = mapDataStack.last();
if (ptr == mapData->end()) {
iteratorStack.removeLast();
mapDataStack.removeLast();
JSObject* object = inputObjectStack.last();
ASSERT(jsDynamicCast<JSSet*>(object) || jsDynamicCast<JSMap*>(object));
propertyStack.append(PropertyNameArray(m_exec));
object->methodTable()->getOwnPropertyNames(object, m_exec, propertyStack.last(), ExcludeDontEnumProperties);
write(NonMapPropertiesTag);
indexStack.append(0);
goto objectStartVisitMember;
}
inValue = ptr.key();
stateStack.append(MapDataEndVisitKey);
goto stateUnknown;
}
case MapDataEndVisitKey: {
inValue = iteratorStack.last().value();
stateStack.append(MapDataEndVisitValue);
goto stateUnknown;
}
case MapDataEndVisitValue: {
++iteratorStack.last();
goto mapDataStartVisitEntry;
}
stateUnknown:
case StateUnknown: {
SerializationReturnCode terminalCode = SuccessfullyCompleted;
if (dumpIfTerminal(inValue, terminalCode)) {
if (terminalCode != SuccessfullyCompleted)
return terminalCode;
break;
}
if (isArray(inValue))
goto arrayStartState;
if (isMap(inValue))
goto mapStartState;
if (isSet(inValue))
goto setStartState;
goto objectStartState;
}
}
if (stateStack.isEmpty())
break;
state = stateStack.last();
stateStack.removeLast();
}
if (m_failed)
return UnspecifiedError;
return SuccessfullyCompleted;
}
typedef Vector<JSC::ArrayBufferContents> ArrayBufferContentsArray;
class CloneDeserializer : CloneBase {
public:
static String deserializeString(const Vector<uint8_t>& buffer)
{
const uint8_t* ptr = buffer.begin();
const uint8_t* end = buffer.end();
uint32_t version;
if (!readLittleEndian(ptr, end, version) || version > CurrentVersion)
return String();
uint8_t tag;
if (!readLittleEndian(ptr, end, tag) || tag != StringTag)
return String();
uint32_t length;
if (!readLittleEndian(ptr, end, length) || length >= StringPoolTag)
return String();
String str;
if (!readString(ptr, end, str, length))
return String();
return String(str.impl());
}
static DeserializationResult deserialize(ExecState* exec, JSGlobalObject* globalObject,
MessagePortArray* messagePorts, ArrayBufferContentsArray* arrayBufferContentsArray,
const Vector<uint8_t>& buffer)
{
if (!buffer.size())
return std::make_pair(jsNull(), UnspecifiedError);
CloneDeserializer deserializer(exec, globalObject, messagePorts, arrayBufferContentsArray, buffer);
if (!deserializer.isValid())
return std::make_pair(JSValue(), ValidationError);
return deserializer.deserialize();
}
private:
struct CachedString {
CachedString(const String& string)
: m_string(string)
{
}
JSValue jsString(ExecState* exec)
{
if (!m_jsString)
m_jsString = JSC::jsString(exec, m_string);
return m_jsString;
}
const String& string() { return m_string; }
private:
String m_string;
JSValue m_jsString;
};
struct CachedStringRef {
CachedStringRef()
: m_base(0)
, m_index(0)
{
}
CachedStringRef(Vector<CachedString>* base, size_t index)
: m_base(base)
, m_index(index)
{
}
CachedString* operator->() { ASSERT(m_base); return &m_base->at(m_index); }
private:
Vector<CachedString>* m_base;
size_t m_index;
};
CloneDeserializer(ExecState* exec, JSGlobalObject* globalObject,
MessagePortArray* messagePorts, ArrayBufferContentsArray* arrayBufferContents,
const Vector<uint8_t>& buffer)
: CloneBase(exec)
, m_globalObject(globalObject)
, m_isDOMGlobalObject(globalObject->inherits(JSDOMGlobalObject::info()))
, m_ptr(buffer.data())
, m_end(buffer.data() + buffer.size())
, m_version(0xFFFFFFFF)
, m_messagePorts(messagePorts)
, m_arrayBufferContents(arrayBufferContents)
, m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0)
{
if (!read(m_version))
m_version = 0xFFFFFFFF;
}
DeserializationResult deserialize();
void throwValidationError()
{
m_exec->vm().throwException(m_exec, createTypeError(m_exec, "Unable to deserialize data."));
}
bool isValid() const { return m_version <= CurrentVersion; }
template <typename T> bool readLittleEndian(T& value)
{
if (m_failed || !readLittleEndian(m_ptr, m_end, value)) {
fail();
return false;
}
return true;
}
#if ASSUME_LITTLE_ENDIAN
template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
{
if (ptr > end - sizeof(value))
return false;
if (sizeof(T) == 1)
value = *ptr++;
else {
value = *reinterpret_cast<const T*>(ptr);
ptr += sizeof(T);
}
return true;
}
#else
template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
{
if (ptr > end - sizeof(value))
return false;
if (sizeof(T) == 1)
value = *ptr++;
else {
value = 0;
for (unsigned i = 0; i < sizeof(T); i++)
value += ((T)*ptr++) << (i * 8);
}
return true;
}
#endif
bool read(uint32_t& i)
{
return readLittleEndian(i);
}
bool read(int32_t& i)
{
return readLittleEndian(*reinterpret_cast<uint32_t*>(&i));
}
bool read(uint16_t& i)
{
return readLittleEndian(i);
}
bool read(uint8_t& i)
{
return readLittleEndian(i);
}
bool read(double& d)
{
union {
double d;
uint64_t i64;
} u;
if (!readLittleEndian(u.i64))
return false;
d = u.d;
return true;
}
bool read(unsigned long long& i)
{
return readLittleEndian(i);
}
bool readStringIndex(uint32_t& i)
{
return readConstantPoolIndex(m_constantPool, i);
}
template <class T> bool readConstantPoolIndex(const T& constantPool, uint32_t& i)
{
if (constantPool.size() <= 0xFF) {
uint8_t i8;
if (!read(i8))
return false;
i = i8;
return true;
}
if (constantPool.size() <= 0xFFFF) {
uint16_t i16;
if (!read(i16))
return false;
i = i16;
return true;
}
return read(i);
}
static bool readString(const uint8_t*& ptr, const uint8_t* end, String& str, unsigned length)
{
if (length >= std::numeric_limits<int32_t>::max() / sizeof(UChar))
return false;
unsigned size = length * sizeof(UChar);
if ((end - ptr) < static_cast<int>(size))
return false;
#if ASSUME_LITTLE_ENDIAN
str = String(reinterpret_cast<const UChar*>(ptr), length);
ptr += length * sizeof(UChar);
#else
Vector<UChar> buffer;
buffer.reserveCapacity(length);
for (unsigned i = 0; i < length; i++) {
uint16_t ch;
readLittleEndian(ptr, end, ch);
buffer.append(ch);
}
str = String::adopt(buffer);
#endif
return true;
}
bool readStringData(CachedStringRef& cachedString)
{
bool scratch;
return readStringData(cachedString, scratch);
}
bool readStringData(CachedStringRef& cachedString, bool& wasTerminator)
{
if (m_failed)
return false;
uint32_t length = 0;
if (!read(length))
return false;
if (length == TerminatorTag) {
wasTerminator = true;
return false;
}
if (length == StringPoolTag) {
unsigned index = 0;
if (!readStringIndex(index)) {
fail();
return false;
}
if (index >= m_constantPool.size()) {
fail();
return false;
}
cachedString = CachedStringRef(&m_constantPool, index);
return true;
}
String str;
if (!readString(m_ptr, m_end, str, length)) {
fail();
return false;
}
m_constantPool.append(str);
cachedString = CachedStringRef(&m_constantPool, m_constantPool.size() - 1);
return true;
}
SerializationTag readTag()
{
if (m_ptr >= m_end)
return ErrorTag;
return static_cast<SerializationTag>(*m_ptr++);
}
bool readArrayBufferViewSubtag(ArrayBufferViewSubtag& tag)
{
if (m_ptr >= m_end)
return false;
tag = static_cast<ArrayBufferViewSubtag>(*m_ptr++);
return true;
}
void putProperty(JSObject* object, unsigned index, JSValue value)
{
object->putDirectIndex(m_exec, index, value);
}
void putProperty(JSObject* object, const Identifier& property, JSValue value)
{
object->putDirectMayBeIndex(m_exec, property, value);
}
bool readFile(RefPtr<File>& file)
{
CachedStringRef path;
if (!readStringData(path))
return 0;
CachedStringRef url;
if (!readStringData(url))
return 0;
CachedStringRef type;
if (!readStringData(type))
return 0;
if (m_isDOMGlobalObject)
file = File::create(path->string(), URL(URL(), url->string()), type->string());
return true;
}
bool readArrayBuffer(RefPtr<ArrayBuffer>& arrayBuffer)
{
uint32_t length;
if (!read(length))
return false;
if (m_ptr + length > m_end)
return false;
arrayBuffer = ArrayBuffer::create(m_ptr, length);
m_ptr += length;
return true;
}
bool readArrayBufferView(JSValue& arrayBufferView)
{
ArrayBufferViewSubtag arrayBufferViewSubtag;
if (!readArrayBufferViewSubtag(arrayBufferViewSubtag))
return false;
uint32_t byteOffset;
if (!read(byteOffset))
return false;
uint32_t byteLength;
if (!read(byteLength))
return false;
JSObject* arrayBufferObj = asObject(readTerminal());
if (!arrayBufferObj || !arrayBufferObj->inherits(JSArrayBuffer::info()))
return false;
unsigned elementSize = typedArrayElementSize(arrayBufferViewSubtag);
if (!elementSize)
return false;
unsigned length = byteLength / elementSize;
if (length * elementSize != byteLength)
return false;
RefPtr<ArrayBuffer> arrayBuffer = toArrayBuffer(arrayBufferObj);
switch (arrayBufferViewSubtag) {
case DataViewTag:
arrayBufferView = getJSValue(DataView::create(arrayBuffer, byteOffset, length).get());
return true;
case Int8ArrayTag:
arrayBufferView = getJSValue(Int8Array::create(arrayBuffer, byteOffset, length).get());
return true;
case Uint8ArrayTag:
arrayBufferView = getJSValue(Uint8Array::create(arrayBuffer, byteOffset, length).get());
return true;
case Uint8ClampedArrayTag:
arrayBufferView = getJSValue(Uint8ClampedArray::create(arrayBuffer, byteOffset, length).get());
return true;
case Int16ArrayTag:
arrayBufferView = getJSValue(Int16Array::create(arrayBuffer, byteOffset, length).get());
return true;
case Uint16ArrayTag:
arrayBufferView = getJSValue(Uint16Array::create(arrayBuffer, byteOffset, length).get());
return true;
case Int32ArrayTag:
arrayBufferView = getJSValue(Int32Array::create(arrayBuffer, byteOffset, length).get());
return true;
case Uint32ArrayTag:
arrayBufferView = getJSValue(Uint32Array::create(arrayBuffer, byteOffset, length).get());
return true;
case Float32ArrayTag:
arrayBufferView = getJSValue(Float32Array::create(arrayBuffer, byteOffset, length).get());
return true;
case Float64ArrayTag:
arrayBufferView = getJSValue(Float64Array::create(arrayBuffer, byteOffset, length).get());
return true;
default:
return false;
}
}
bool read(Vector<uint8_t>& result)
{
ASSERT(result.isEmpty());
uint32_t size;
if (!read(size))
return false;
if (m_ptr + size > m_end)
return false;
result.append(m_ptr, size);
m_ptr += size;
return true;
}
#if ENABLE(SUBTLE_CRYPTO)
bool read(CryptoAlgorithmIdentifier& result)
{
uint8_t algorithmTag;
if (!read(algorithmTag))
return false;
if (algorithmTag > cryptoAlgorithmIdentifierTagMaximumValue)
return false;
switch (static_cast<CryptoAlgorithmIdentifierTag>(algorithmTag)) {
case CryptoAlgorithmIdentifierTag::RSAES_PKCS1_v1_5:
result = CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5;
break;
case CryptoAlgorithmIdentifierTag::RSASSA_PKCS1_v1_5:
result = CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5;
break;
case CryptoAlgorithmIdentifierTag::RSA_PSS:
result = CryptoAlgorithmIdentifier::RSA_PSS;
break;
case CryptoAlgorithmIdentifierTag::RSA_OAEP:
result = CryptoAlgorithmIdentifier::RSA_OAEP;
break;
case CryptoAlgorithmIdentifierTag::ECDSA:
result = CryptoAlgorithmIdentifier::ECDSA;
break;
case CryptoAlgorithmIdentifierTag::ECDH:
result = CryptoAlgorithmIdentifier::ECDH;
break;
case CryptoAlgorithmIdentifierTag::AES_CTR:
result = CryptoAlgorithmIdentifier::AES_CTR;
break;
case CryptoAlgorithmIdentifierTag::AES_CBC:
result = CryptoAlgorithmIdentifier::AES_CBC;
break;
case CryptoAlgorithmIdentifierTag::AES_CMAC:
result = CryptoAlgorithmIdentifier::AES_CMAC;
break;
case CryptoAlgorithmIdentifierTag::AES_GCM:
result = CryptoAlgorithmIdentifier::AES_GCM;
break;
case CryptoAlgorithmIdentifierTag::AES_CFB:
result = CryptoAlgorithmIdentifier::AES_CFB;
break;
case CryptoAlgorithmIdentifierTag::AES_KW:
result = CryptoAlgorithmIdentifier::AES_KW;
break;
case CryptoAlgorithmIdentifierTag::HMAC:
result = CryptoAlgorithmIdentifier::HMAC;
break;
case CryptoAlgorithmIdentifierTag::DH:
result = CryptoAlgorithmIdentifier::DH;
break;
case CryptoAlgorithmIdentifierTag::SHA_1:
result = CryptoAlgorithmIdentifier::SHA_1;
break;
case CryptoAlgorithmIdentifierTag::SHA_224:
result = CryptoAlgorithmIdentifier::SHA_224;
break;
case CryptoAlgorithmIdentifierTag::SHA_256:
result = CryptoAlgorithmIdentifier::SHA_256;
break;
case CryptoAlgorithmIdentifierTag::SHA_384:
result = CryptoAlgorithmIdentifier::SHA_384;
break;
case CryptoAlgorithmIdentifierTag::SHA_512:
result = CryptoAlgorithmIdentifier::SHA_512;
break;
case CryptoAlgorithmIdentifierTag::CONCAT:
result = CryptoAlgorithmIdentifier::CONCAT;
break;
case CryptoAlgorithmIdentifierTag::HKDF_CTR:
result = CryptoAlgorithmIdentifier::HKDF_CTR;
break;
case CryptoAlgorithmIdentifierTag::PBKDF2:
result = CryptoAlgorithmIdentifier::PBKDF2;
break;
}
return true;
}
bool read(CryptoKeyClassSubtag& result)
{
uint8_t tag;
if (!read(tag))
return false;
if (tag > cryptoKeyClassSubtagMaximumValue)
return false;
result = static_cast<CryptoKeyClassSubtag>(tag);
return true;
}
bool read(CryptoKeyUsageTag& result)
{
uint8_t tag;
if (!read(tag))
return false;
if (tag > cryptoKeyUsageTagMaximumValue)
return false;
result = static_cast<CryptoKeyUsageTag>(tag);
return true;
}
bool read(CryptoKeyAsymmetricTypeSubtag& result)
{
uint8_t tag;
if (!read(tag))
return false;
if (tag > cryptoKeyAsymmetricTypeSubtagMaximumValue)
return false;
result = static_cast<CryptoKeyAsymmetricTypeSubtag>(tag);
return true;
}
bool readHMACKey(bool extractable, CryptoKeyUsage usages, RefPtr<CryptoKey>& result)
{
Vector<uint8_t> keyData;
if (!read(keyData))
return false;
CryptoAlgorithmIdentifier hash;
if (!read(hash))
return false;
result = CryptoKeyHMAC::create(keyData, hash, extractable, usages);
return true;
}
bool readAESKey(bool extractable, CryptoKeyUsage usages, RefPtr<CryptoKey>& result)
{
CryptoAlgorithmIdentifier algorithm;
if (!read(algorithm))
return false;
if (!CryptoKeyAES::isValidAESAlgorithm(algorithm))
return false;
Vector<uint8_t> keyData;
if (!read(keyData))
return false;
result = CryptoKeyAES::create(algorithm, keyData, extractable, usages);
return true;
}
bool readRSAKey(bool extractable, CryptoKeyUsage usages, RefPtr<CryptoKey>& result)
{
CryptoAlgorithmIdentifier algorithm;
if (!read(algorithm))
return false;
int32_t isRestrictedToHash;
CryptoAlgorithmIdentifier hash;
if (!read(isRestrictedToHash))
return false;
if (isRestrictedToHash && !read(hash))
return false;
CryptoKeyAsymmetricTypeSubtag type;
if (!read(type))
return false;
Vector<uint8_t> modulus;
if (!read(modulus))
return false;
Vector<uint8_t> exponent;
if (!read(exponent))
return false;
if (type == CryptoKeyAsymmetricTypeSubtag::Public) {
auto keyData = CryptoKeyDataRSAComponents::createPublic(modulus, exponent);
auto key = CryptoKeyRSA::create(algorithm, *keyData, extractable, usages);
if (isRestrictedToHash)
key->restrictToHash(hash);
result = std::move(key);
return true;
}
Vector<uint8_t> privateExponent;
if (!read(privateExponent))
return false;
uint32_t primeCount;
if (!read(primeCount))
return false;
if (!primeCount) {
auto keyData = CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
auto key = CryptoKeyRSA::create(algorithm, *keyData, extractable, usages);
if (isRestrictedToHash)
key->restrictToHash(hash);
result = std::move(key);
return true;
}
if (primeCount < 2)
return false;
CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo;
CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo;
Vector<CryptoKeyDataRSAComponents::PrimeInfo> otherPrimeInfos(primeCount - 2);
if (!read(firstPrimeInfo.primeFactor))
return false;
if (!read(firstPrimeInfo.factorCRTExponent))
return false;
if (!read(secondPrimeInfo.primeFactor))
return false;
if (!read(secondPrimeInfo.factorCRTExponent))
return false;
if (!read(secondPrimeInfo.factorCRTCoefficient))
return false;
for (unsigned i = 2; i < primeCount; ++i) {
if (!read(otherPrimeInfos[i].primeFactor))
return false;
if (!read(otherPrimeInfos[i].factorCRTExponent))
return false;
if (!read(otherPrimeInfos[i].factorCRTCoefficient))
return false;
}
auto keyData = CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
auto key = CryptoKeyRSA::create(algorithm, *keyData, extractable, usages);
if (isRestrictedToHash)
key->restrictToHash(hash);
result = std::move(key);
return true;
}
bool readCryptoKey(JSValue& cryptoKey)
{
int32_t extractable;
if (!read(extractable))
return false;
uint32_t usagesCount;
if (!read(usagesCount))
return false;
CryptoKeyUsage usages = 0;
for (uint32_t i = 0; i < usagesCount; ++i) {
CryptoKeyUsageTag usage;
if (!read(usage))
return false;
switch (usage) {
case CryptoKeyUsageTag::Encrypt:
usages |= CryptoKeyUsageEncrypt;
break;
case CryptoKeyUsageTag::Decrypt:
usages |= CryptoKeyUsageDecrypt;
break;
case CryptoKeyUsageTag::Sign:
usages |= CryptoKeyUsageSign;
break;
case CryptoKeyUsageTag::Verify:
usages |= CryptoKeyUsageVerify;
break;
case CryptoKeyUsageTag::DeriveKey:
usages |= CryptoKeyUsageDeriveKey;
break;
case CryptoKeyUsageTag::DeriveBits:
usages |= CryptoKeyUsageDeriveBits;
break;
case CryptoKeyUsageTag::WrapKey:
usages |= CryptoKeyUsageWrapKey;
break;
case CryptoKeyUsageTag::UnwrapKey:
usages |= CryptoKeyUsageUnwrapKey;
break;
}
}
CryptoKeyClassSubtag cryptoKeyClass;
if (!read(cryptoKeyClass))
return false;
RefPtr<CryptoKey> result;
switch (cryptoKeyClass) {
case CryptoKeyClassSubtag::HMAC:
if (!readHMACKey(extractable, usages, result))
return false;
break;
case CryptoKeyClassSubtag::AES:
if (!readAESKey(extractable, usages, result))
return false;
break;
case CryptoKeyClassSubtag::RSA:
if (!readRSAKey(extractable, usages, result))
return false;
break;
}
cryptoKey = getJSValue(result.get());
return true;
}
#endif
template<class T>
JSValue getJSValue(T* nativeObj)
{
return toJS(m_exec, jsCast<JSDOMGlobalObject*>(m_globalObject), nativeObj);
}
JSValue readTerminal()
{
SerializationTag tag = readTag();
switch (tag) {
case UndefinedTag:
return jsUndefined();
case NullTag:
return jsNull();
case IntTag: {
int32_t i;
if (!read(i))
return JSValue();
return jsNumber(i);
}
case ZeroTag:
return jsNumber(0);
case OneTag:
return jsNumber(1);
case FalseTag:
return jsBoolean(false);
case TrueTag:
return jsBoolean(true);
case FalseObjectTag: {
BooleanObject* obj = BooleanObject::create(m_exec->vm(), m_globalObject->booleanObjectStructure());
obj->setInternalValue(m_exec->vm(), jsBoolean(false));
m_gcBuffer.append(obj);
return obj;
}
case TrueObjectTag: {
BooleanObject* obj = BooleanObject::create(m_exec->vm(), m_globalObject->booleanObjectStructure());
obj->setInternalValue(m_exec->vm(), jsBoolean(true));
m_gcBuffer.append(obj);
return obj;
}
case DoubleTag: {
double d;
if (!read(d))
return JSValue();
return jsNumber(d);
}
case NumberObjectTag: {
double d;
if (!read(d))
return JSValue();
NumberObject* obj = constructNumber(m_exec, m_globalObject, jsNumber(d));
m_gcBuffer.append(obj);
return obj;
}
case DateTag: {
double d;
if (!read(d))
return JSValue();
return DateInstance::create(m_exec->vm(), m_globalObject->dateStructure(), d);
}
case FileTag: {
RefPtr<File> file;
if (!readFile(file))
return JSValue();
if (!m_isDOMGlobalObject)
return jsNull();
return toJS(m_exec, jsCast<JSDOMGlobalObject*>(m_globalObject), file.get());
}
case FileListTag: {
unsigned length = 0;
if (!read(length))
return JSValue();
RefPtr<FileList> result = FileList::create();
for (unsigned i = 0; i < length; i++) {
RefPtr<File> file;
if (!readFile(file))
return JSValue();
if (m_isDOMGlobalObject)
result->append(file.get());
}
if (!m_isDOMGlobalObject)
return jsNull();
return getJSValue(result.get());
}
case ImageDataTag: {
int32_t width;
if (!read(width))
return JSValue();
int32_t height;
if (!read(height))
return JSValue();
uint32_t length;
if (!read(length))
return JSValue();
if (m_end < ((uint8_t*)0) + length || m_ptr > m_end - length) {
fail();
return JSValue();
}
if (!m_isDOMGlobalObject) {
m_ptr += length;
return jsNull();
}
RefPtr<ImageData> result = ImageData::create(IntSize(width, height));
memcpy(result->data()->data(), m_ptr, length);
m_ptr += length;
return getJSValue(result.get());
}
case BlobTag: {
CachedStringRef url;
if (!readStringData(url))
return JSValue();
CachedStringRef type;
if (!readStringData(type))
return JSValue();
unsigned long long size = 0;
if (!read(size))
return JSValue();
if (!m_isDOMGlobalObject)
return jsNull();
return getJSValue(Blob::create(URL(URL(), url->string()), type->string(), size).get());
}
case StringTag: {
CachedStringRef cachedString;
if (!readStringData(cachedString))
return JSValue();
return cachedString->jsString(m_exec);
}
case EmptyStringTag:
return jsEmptyString(&m_exec->vm());
case StringObjectTag: {
CachedStringRef cachedString;
if (!readStringData(cachedString))
return JSValue();
StringObject* obj = constructString(m_exec->vm(), m_globalObject, cachedString->jsString(m_exec));
m_gcBuffer.append(obj);
return obj;
}
case EmptyStringObjectTag: {
VM& vm = m_exec->vm();
StringObject* obj = constructString(vm, m_globalObject, jsEmptyString(&vm));
m_gcBuffer.append(obj);
return obj;
}
case RegExpTag: {
CachedStringRef pattern;
if (!readStringData(pattern))
return JSValue();
CachedStringRef flags;
if (!readStringData(flags))
return JSValue();
RegExpFlags reFlags = regExpFlags(flags->string());
ASSERT(reFlags != InvalidFlags);
VM& vm = m_exec->vm();
RegExp* regExp = RegExp::create(vm, pattern->string(), reFlags);
return RegExpObject::create(vm, m_globalObject->regExpStructure(), regExp);
}
case ObjectReferenceTag: {
unsigned index = 0;
if (!readConstantPoolIndex(m_gcBuffer, index)) {
fail();
return JSValue();
}
return m_gcBuffer.at(index);
}
case MessagePortReferenceTag: {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || !m_messagePorts || index >= m_messagePorts->size()) {
fail();
return JSValue();
}
return getJSValue(m_messagePorts->at(index).get());
}
case ArrayBufferTag: {
RefPtr<ArrayBuffer> arrayBuffer;
if (!readArrayBuffer(arrayBuffer)) {
fail();
return JSValue();
}
JSValue result = getJSValue(arrayBuffer.get());
m_gcBuffer.append(result);
return result;
}
case ArrayBufferTransferTag: {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_arrayBuffers.size()) {
fail();
return JSValue();
}
if (!m_arrayBuffers[index])
m_arrayBuffers[index] = ArrayBuffer::create(m_arrayBufferContents->at(index));
return getJSValue(m_arrayBuffers[index].get());
}
case ArrayBufferViewTag: {
JSValue arrayBufferView;
if (!readArrayBufferView(arrayBufferView)) {
fail();
return JSValue();
}
m_gcBuffer.append(arrayBufferView);
return arrayBufferView;
}
#if ENABLE(SUBTLE_CRYPTO)
case CryptoKeyTag: {
JSValue cryptoKey;
if (!readCryptoKey(cryptoKey)) {
fail();
return JSValue();
}
m_gcBuffer.append(cryptoKey);
return cryptoKey;
}
#endif
default:
m_ptr--; // Push the tag back
return JSValue();
}
}
bool consumeMapDataTerminationIfPossible()
{
if (readTag() == NonMapPropertiesTag)
return true;
m_ptr--;
return false;
}
JSGlobalObject* m_globalObject;
bool m_isDOMGlobalObject;
const uint8_t* m_ptr;
const uint8_t* m_end;
unsigned m_version;
Vector<CachedString> m_constantPool;
MessagePortArray* m_messagePorts;
ArrayBufferContentsArray* m_arrayBufferContents;
ArrayBufferArray m_arrayBuffers;
};
DeserializationResult CloneDeserializer::deserialize()
{
Vector<uint32_t, 16> indexStack;
Vector<Identifier, 16> propertyNameStack;
Vector<JSObject*, 32> outputObjectStack;
Vector<JSValue, 4> keyStack;
Vector<MapData*, 4> mapDataStack;
Vector<WalkerState, 16> stateStack;
WalkerState state = StateUnknown;
JSValue outValue;
while (1) {
switch (state) {
arrayStartState:
case ArrayStartState: {
uint32_t length;
if (!read(length)) {
fail();
goto error;
}
JSArray* outArray = constructEmptyArray(m_exec, 0, m_globalObject, length);
m_gcBuffer.append(outArray);
outputObjectStack.append(outArray);
}
arrayStartVisitMember:
FALLTHROUGH;
case ArrayStartVisitMember: {
uint32_t index;
if (!read(index)) {
fail();
goto error;
}
if (index == TerminatorTag) {
JSObject* outArray = outputObjectStack.last();
outValue = outArray;
outputObjectStack.removeLast();
break;
} else if (index == NonIndexPropertiesTag) {
goto objectStartVisitMember;
}
if (JSValue terminal = readTerminal()) {
putProperty(outputObjectStack.last(), index, terminal);
goto arrayStartVisitMember;
}
if (m_failed)
goto error;
indexStack.append(index);
stateStack.append(ArrayEndVisitMember);
goto stateUnknown;
}
case ArrayEndVisitMember: {
JSObject* outArray = outputObjectStack.last();
putProperty(outArray, indexStack.last(), outValue);
indexStack.removeLast();
goto arrayStartVisitMember;
}
objectStartState:
case ObjectStartState: {
if (outputObjectStack.size() > maximumFilterRecursion)
return std::make_pair(JSValue(), StackOverflowError);
JSObject* outObject = constructEmptyObject(m_exec, m_globalObject->objectPrototype());
m_gcBuffer.append(outObject);
outputObjectStack.append(outObject);
}
objectStartVisitMember:
FALLTHROUGH;
case ObjectStartVisitMember: {
CachedStringRef cachedString;
bool wasTerminator = false;
if (!readStringData(cachedString, wasTerminator)) {
if (!wasTerminator)
goto error;
JSObject* outObject = outputObjectStack.last();
outValue = outObject;
outputObjectStack.removeLast();
break;
}
if (JSValue terminal = readTerminal()) {
putProperty(outputObjectStack.last(), Identifier(m_exec, cachedString->string()), terminal);
goto objectStartVisitMember;
}
stateStack.append(ObjectEndVisitMember);
propertyNameStack.append(Identifier(m_exec, cachedString->string()));
goto stateUnknown;
}
case ObjectEndVisitMember: {
putProperty(outputObjectStack.last(), propertyNameStack.last(), outValue);
propertyNameStack.removeLast();
goto objectStartVisitMember;
}
mapObjectStartState: {
if (outputObjectStack.size() > maximumFilterRecursion)
return std::make_pair(JSValue(), StackOverflowError);
JSMap* map = JSMap::create(m_exec->vm(), m_globalObject->mapStructure());
m_gcBuffer.append(map);
outputObjectStack.append(map);
MapData* mapData = map->mapData();
mapDataStack.append(mapData);
goto mapDataStartVisitEntry;
}
setObjectStartState: {
if (outputObjectStack.size() > maximumFilterRecursion)
return std::make_pair(JSValue(), StackOverflowError);
JSSet* set = JSSet::create(m_exec->vm(), m_globalObject->setStructure());
m_gcBuffer.append(set);
outputObjectStack.append(set);
MapData* mapData = set->mapData();
mapDataStack.append(mapData);
goto mapDataStartVisitEntry;
}
mapDataStartVisitEntry:
case MapDataStartVisitEntry: {
if (consumeMapDataTerminationIfPossible()) {
mapDataStack.removeLast();
goto objectStartVisitMember;
}
stateStack.append(MapDataEndVisitKey);
goto stateUnknown;
}
case MapDataEndVisitKey: {
keyStack.append(outValue);
stateStack.append(MapDataEndVisitValue);
goto stateUnknown;
}
case MapDataEndVisitValue: {
mapDataStack.last()->set(m_exec, keyStack.last(), outValue);
keyStack.removeLast();
goto mapDataStartVisitEntry;
}
stateUnknown:
case StateUnknown:
if (JSValue terminal = readTerminal()) {
outValue = terminal;
break;
}
SerializationTag tag = readTag();
if (tag == ArrayTag)
goto arrayStartState;
if (tag == ObjectTag)
goto objectStartState;
if (tag == MapObjectTag)
goto mapObjectStartState;
if (tag == SetObjectTag)
goto setObjectStartState;
goto error;
}
if (stateStack.isEmpty())
break;
state = stateStack.last();
stateStack.removeLast();
}
ASSERT(outValue);
ASSERT(!m_failed);
return std::make_pair(outValue, SuccessfullyCompleted);
error:
fail();
return std::make_pair(JSValue(), ValidationError);
}
SerializedScriptValue::~SerializedScriptValue()
{
}
SerializedScriptValue::SerializedScriptValue(const Vector<uint8_t>& buffer)
: m_data(buffer)
{
}
SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>& buffer)
{
m_data.swap(buffer);
}
SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>& buffer, Vector<String>& blobURLs)
{
m_data.swap(buffer);
m_blobURLs.swap(blobURLs);
}
SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>& buffer, Vector<String>& blobURLs, PassOwnPtr<ArrayBufferContentsArray> arrayBufferContentsArray)
: m_arrayBufferContentsArray(arrayBufferContentsArray)
{
m_data.swap(buffer);
m_blobURLs.swap(blobURLs);
}
PassOwnPtr<SerializedScriptValue::ArrayBufferContentsArray> SerializedScriptValue::transferArrayBuffers(
ExecState* exec, ArrayBufferArray& arrayBuffers, SerializationReturnCode& code)
{
for (size_t i = 0; i < arrayBuffers.size(); i++) {
if (arrayBuffers[i]->isNeutered()) {
code = ValidationError;
return nullptr;
}
}
OwnPtr<ArrayBufferContentsArray> contents = adoptPtr(new ArrayBufferContentsArray(arrayBuffers.size()));
Vector<Ref<DOMWrapperWorld>> worlds;
static_cast<WebCoreJSClientData*>(exec->vm().clientData)->getAllWorlds(worlds);
HashSet<JSC::ArrayBuffer*> visited;
for (size_t arrayBufferIndex = 0; arrayBufferIndex < arrayBuffers.size(); arrayBufferIndex++) {
if (visited.contains(arrayBuffers[arrayBufferIndex].get()))
continue;
visited.add(arrayBuffers[arrayBufferIndex].get());
bool result = arrayBuffers[arrayBufferIndex]->transfer(contents->at(arrayBufferIndex));
if (!result) {
code = ValidationError;
return nullptr;
}
}
return contents.release();
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(ExecState* exec, JSValue value,
MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers,
SerializationErrorMode throwExceptions)
{
Vector<uint8_t> buffer;
Vector<String> blobURLs;
SerializationReturnCode code = CloneSerializer::serialize(exec, value, messagePorts, arrayBuffers, blobURLs, buffer);
OwnPtr<ArrayBufferContentsArray> arrayBufferContentsArray;
if (arrayBuffers && serializationDidCompleteSuccessfully(code))
arrayBufferContentsArray = transferArrayBuffers(exec, *arrayBuffers, code);
if (throwExceptions == Throwing)
maybeThrowExceptionIfSerializationFailed(exec, code);
if (!serializationDidCompleteSuccessfully(code))
return 0;
return adoptRef(new SerializedScriptValue(buffer, blobURLs, arrayBufferContentsArray.release()));
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(const String& string)
{
Vector<uint8_t> buffer;
if (!CloneSerializer::serialize(string, buffer))
return 0;
return adoptRef(new SerializedScriptValue(buffer));
}
#if ENABLE(INDEXED_DATABASE)
PassRefPtr<SerializedScriptValue> SerializedScriptValue::numberValue(double value)
{
Vector<uint8_t> buffer;
CloneSerializer::serializeNumber(value, buffer);
return adoptRef(new SerializedScriptValue(buffer));
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::undefinedValue()
{
Vector<uint8_t> buffer;
CloneSerializer::serializeUndefined(buffer);
return adoptRef(new SerializedScriptValue(buffer));
}
#endif
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, JSValueRef* exception)
{
ExecState* exec = toJS(originContext);
APIEntryShim entryShim(exec);
JSValue value = toJS(exec, apiValue);
RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(exec, value, nullptr, nullptr);
if (exec->hadException()) {
if (exception)
*exception = toRef(exec, exec->exception());
exec->clearException();
return 0;
}
ASSERT(serializedValue);
return serializedValue.release();
}
String SerializedScriptValue::toString()
{
return CloneDeserializer::deserializeString(m_data);
}
JSValue SerializedScriptValue::deserialize(ExecState* exec, JSGlobalObject* globalObject,
MessagePortArray* messagePorts, SerializationErrorMode throwExceptions)
{
DeserializationResult result = CloneDeserializer::deserialize(exec, globalObject, messagePorts,
m_arrayBufferContentsArray.get(), m_data);
if (throwExceptions == Throwing)
maybeThrowExceptionIfSerializationFailed(exec, result.second);
return result.first;
}
JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception)
{
ExecState* exec = toJS(destinationContext);
APIEntryShim entryShim(exec);
JSValue value = deserialize(exec, exec->lexicalGlobalObject(), nullptr);
if (exec->hadException()) {
if (exception)
*exception = toRef(exec, exec->exception());
exec->clearException();
return 0;
}
ASSERT(value);
return toRef(exec, value);
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::nullValue()
{
Vector<uint8_t> buffer;
return adoptRef(new SerializedScriptValue(buffer));
}
void SerializedScriptValue::maybeThrowExceptionIfSerializationFailed(ExecState* exec, SerializationReturnCode code)
{
if (code == SuccessfullyCompleted)
return;
switch (code) {
case StackOverflowError:
exec->vm().throwException(exec, createStackOverflowError(exec));
break;
case ValidationError:
exec->vm().throwException(exec, createTypeError(exec, "Unable to deserialize data."));
break;
case DataCloneError:
setDOMException(exec, DATA_CLONE_ERR);
break;
case ExistingExceptionError:
break;
case UnspecifiedError:
break;
default:
ASSERT_NOT_REACHED();
}
}
bool SerializedScriptValue::serializationDidCompleteSuccessfully(SerializationReturnCode code)
{
return (code == SuccessfullyCompleted);
}
uint32_t SerializedScriptValue::wireFormatVersion()
{
return CurrentVersion;
}
}