diff options
Diffstat (limited to 'Source/WebCore/bindings/js/SerializedScriptValue.cpp')
-rw-r--r-- | Source/WebCore/bindings/js/SerializedScriptValue.cpp | 1110 |
1 files changed, 674 insertions, 436 deletions
diff --git a/Source/WebCore/bindings/js/SerializedScriptValue.cpp b/Source/WebCore/bindings/js/SerializedScriptValue.cpp index 1f7919289..c9904c091 100644 --- a/Source/WebCore/bindings/js/SerializedScriptValue.cpp +++ b/Source/WebCore/bindings/js/SerializedScriptValue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2013, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 + * THIS SOFTWARE IS PROVIDED BY APPLE 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 + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE 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 @@ -28,49 +28,56 @@ #include "SerializedScriptValue.h" #include "Blob.h" +#include "BlobRegistry.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 "IDBValue.h" #include "ImageData.h" #include "JSBlob.h" #include "JSCryptoKey.h" +#include "JSDOMBinding.h" #include "JSDOMGlobalObject.h" #include "JSFile.h" #include "JSFileList.h" #include "JSImageData.h" #include "JSMessagePort.h" #include "JSNavigator.h" -#include "NotImplemented.h" +#include "ScriptExecutionContext.h" +#include "ScriptState.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/Exception.h> #include <runtime/ExceptionHelpers.h> +#include <runtime/IterationKind.h> #include <runtime/JSArrayBuffer.h> #include <runtime/JSArrayBufferView.h> +#include <runtime/JSCInlines.h> #include <runtime/JSDataView.h> #include <runtime/JSMap.h> +#include <runtime/JSMapIterator.h> #include <runtime/JSSet.h> +#include <runtime/JSSetIterator.h> #include <runtime/JSTypedArrays.h> -#include <runtime/MapData.h> #include <runtime/ObjectConstructor.h> -#include <runtime/Operations.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/MainThread.h> +#include <wtf/RunLoop.h> #include <wtf/Vector.h> using namespace JSC; @@ -85,9 +92,20 @@ namespace WebCore { static const unsigned maximumFilterRecursion = 40000; +enum class SerializationReturnCode { + SuccessfullyCompleted, + StackOverflowError, + InterruptedExecutionError, + ValidationError, + ExistingExceptionError, + DataCloneError, + UnspecifiedError +}; + enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember, ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember, - MapDataStartVisitEntry, MapDataEndVisitKey, MapDataEndVisitValue }; + MapDataStartVisitEntry, MapDataEndVisitKey, MapDataEndVisitValue, + SetDataStartVisitEntry, SetDataEndVisitKey }; // These can't be reordered, and any new types must be added to the end of the list enum SerializationTag { @@ -122,9 +140,11 @@ enum SerializationTag { SetObjectTag = 29, MapObjectTag = 30, NonMapPropertiesTag = 31, + NonSetPropertiesTag = 32, #if ENABLE(SUBTLE_CRYPTO) - CryptoKeyTag = 32, + CryptoKeyTag = 33, #endif + SharedArrayBufferTag = 34, ErrorTag = 255 }; @@ -166,6 +186,8 @@ static unsigned typedArrayElementSize(ArrayBufferViewSubtag tag) #if ENABLE(SUBTLE_CRYPTO) +const uint32_t currentKeyFormatVersion = 1; + enum class CryptoKeyClassSubtag { HMAC = 0, AES = 1, @@ -217,7 +239,7 @@ enum class CryptoAlgorithmIdentifierTag { }; const uint8_t cryptoAlgorithmIdentifierTagMaximumValue = 21; -static unsigned countUsages(CryptoKeyUsage usages) +static unsigned countUsages(CryptoKeyUsageBitmap usages) { // Fast bit count algorithm for sparse bit maps. unsigned count = 0; @@ -239,12 +261,16 @@ static unsigned countUsages(CryptoKeyUsage usages) * 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. + * Version 6. added support for 8-bit strings. */ -static const unsigned CurrentVersion = 5; +static const unsigned CurrentVersion = 6; static const unsigned TerminatorTag = 0xFFFFFFFF; static const unsigned StringPoolTag = 0xFFFFFFFE; static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD; +// The high bit of a StringData's length determines the character size. +static const unsigned StringDataIs8BitFlag = 0x80000000; + /* * Object serialization is performed according to the following grammar, all tags * are recorded as a single uint8_t. @@ -264,9 +290,10 @@ static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD; * * Map :- MapObjectTag MapData * - * Set :- SetObjectTag MapData + * Set :- SetObjectTag SetData * - * MapData :- (<key:Value><value:Value>) NonMapPropertiesTag (<name:StringData><value:Value>)* TerminatorTag + * MapData :- (<key:Value><value:Value>)* NonMapPropertiesTag (<name:StringData><value:Value>)* TerminatorTag + * SetData :- (<key:Value>)* NonSetPropertiesTag (<name:StringData><value:Value>)* TerminatorTag * * Terminal :- * UndefinedTag @@ -293,7 +320,11 @@ static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD; * | 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) + * | CryptoKeyTag <wrappedKeyLength:uint32_t> <factor:byte{wrappedKeyLength}> + * + * Inside wrapped crypto key, data is serialized in this format: + * + * <keyFormatVersion:uint32_t> <extractable:int32_t> <usagesCount:uint32_t> <usages:byte{usagesCount}> CryptoKeyClassSubtag (CryptoKeyHMAC | CryptoKeyAES | CryptoKeyRSA) * * String :- * EmptyStringTag @@ -305,13 +336,13 @@ static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD; * * StringData :- * StringPoolTag <cpIndex:IndexType> - * (not (TerminatorTag | StringPoolTag))<length:uint32_t><characters:UChar{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed + * (not (TerminatorTag | StringPoolTag))<is8Bit:uint32_t:1><length:uint32_t:31><characters:CharType{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed * * File :- * FileTag FileData * * FileData :- - * <path:StringData> <url:StringData> <type:StringData> + * <path:StringData> <url:StringData> <type:StringData> <name:StringData> * * FileList :- * FileListTag <length:uint32_t>(<file:FileData>){length} @@ -354,7 +385,7 @@ static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD; * <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; +using DeserializationResult = std::pair<JSC::JSValue, SerializationReturnCode>; class CloneBase { protected: @@ -366,18 +397,13 @@ protected: bool shouldTerminate() { - return m_exec->hadException(); - } - - void throwStackOverflow() - { - m_exec->vm().throwException(m_exec, createStackOverflowError(m_exec)); + VM& vm = m_exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return scope.exception(); } - NO_RETURN_DUE_TO_ASSERT void fail() { - ASSERT_NOT_REACHED(); m_failed = true; } @@ -386,6 +412,24 @@ protected: MarkedArgumentBuffer m_gcBuffer; }; +#if ENABLE(SUBTLE_CRYPTO) +static bool wrapCryptoKey(ExecState* exec, const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) +{ + ScriptExecutionContext* scriptExecutionContext = scriptExecutionContextFromExecState(exec); + if (!scriptExecutionContext) + return false; + return scriptExecutionContext->wrapCryptoKey(key, wrappedKey); +} + +static bool unwrapCryptoKey(ExecState* exec, const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) +{ + ScriptExecutionContext* scriptExecutionContext = scriptExecutionContextFromExecState(exec); + if (!scriptExecutionContext) + return false; + return scriptExecutionContext->unwrapCryptoKey(wrappedKey, key); +} +#endif + #if ASSUME_LITTLE_ENDIAN template <typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value) { @@ -433,24 +477,26 @@ template <> bool writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, const uint8 class CloneSerializer : CloneBase { public: - static SerializationReturnCode serialize(ExecState* exec, JSValue value, - MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, - Vector<String>& blobURLs, Vector<uint8_t>& out) + static SerializationReturnCode serialize(ExecState* exec, JSValue value, Vector<RefPtr<MessagePort>>& messagePorts, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers, Vector<String>& blobURLs, Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers) { - CloneSerializer serializer(exec, messagePorts, arrayBuffers, blobURLs, out); + CloneSerializer serializer(exec, messagePorts, arrayBuffers, blobURLs, out, context, sharedBuffers); return serializer.serialize(value); } - static bool serialize(const String& s, Vector<uint8_t>& out) + static bool serialize(StringView string, Vector<uint8_t>& out) { writeLittleEndian(out, CurrentVersion); - if (s.isEmpty()) { + if (string.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()); + if (string.is8Bit()) { + writeLittleEndian(out, string.length() | StringDataIs8BitFlag); + return writeLittleEndian(out, string.characters8(), string.length()); + } + writeLittleEndian(out, string.length()); + return writeLittleEndian(out, string.characters16(), string.length()); } static void serializeUndefined(Vector<uint8_t>& out) @@ -480,11 +526,13 @@ public: private: typedef HashMap<JSObject*, uint32_t> ObjectPool; - CloneSerializer(ExecState* exec, MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, Vector<String>& blobURLs, Vector<uint8_t>& out) + CloneSerializer(ExecState* exec, Vector<RefPtr<MessagePort>>& messagePorts, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers, Vector<String>& blobURLs, Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers) : CloneBase(exec) , m_buffer(out) , m_blobURLs(blobURLs) - , m_emptyIdentifier(exec, emptyString()) + , m_emptyIdentifier(Identifier::fromString(exec, emptyString())) + , m_context(context) + , m_sharedBuffers(sharedBuffers) { write(CurrentVersion); fillTransferMap(messagePorts, m_transferredMessagePorts); @@ -492,13 +540,13 @@ private: } template <class T> - void fillTransferMap(Vector<RefPtr<T>, 1>* input, ObjectPool& result) + void fillTransferMap(Vector<RefPtr<T>>& input, ObjectPool& result) { - if (!input) + if (input.isEmpty()) 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()); + for (size_t i = 0; i < input.size(); i++) { + JSC::JSValue value = toJS(m_exec, globalObject, input[i].get()); JSC::JSObject* obj = value.getObject(); if (obj && !result.contains(obj)) result.add(obj, i); @@ -507,27 +555,27 @@ private: SerializationReturnCode serialize(JSValue in); - bool isArray(JSValue value) + bool isArray(VM& vm, JSValue value) { if (!value.isObject()) return false; JSObject* object = asObject(value); - return isJSArray(object) || object->inherits(JSArray::info()); + return isJSArray(object) || object->inherits(vm, JSArray::info()); } - bool isMap(JSValue value) + bool isMap(VM& vm, JSValue value) { if (!value.isObject()) return false; JSObject* object = asObject(value); - return object->inherits(JSMap::info()); + return object->inherits(vm, JSMap::info()); } - bool isSet(JSValue value) + bool isSet(VM& vm, JSValue value) { if (!value.isObject()) return false; JSObject* object = asObject(value); - return object->inherits(JSSet::info()); + return object->inherits(vm, JSSet::info()); } bool checkForDuplicate(JSObject* object) @@ -538,7 +586,7 @@ private: // Handle duplicate references if (found != m_objectPool.end()) { write(ObjectReferenceTag); - ASSERT(static_cast<int32_t>(found->value) < m_objectPool.size()); + ASSERT(found->value < m_objectPool.size()); writeObjectIndex(found->value); return true; } @@ -604,7 +652,7 @@ private: JSValue getProperty(JSObject* object, const Identifier& propertyName) { - PropertySlot slot(object); + PropertySlot slot(object, PropertySlot::InternalMethodType::Get); if (object->methodTable()->getOwnPropertySlot(object, m_exec, propertyName, slot)) return slot.getValue(m_exec, propertyName); return JSValue(); @@ -638,58 +686,59 @@ private: } } - void dumpString(String str) + void dumpString(const String& string) { - if (str.isEmpty()) + if (string.isEmpty()) write(EmptyStringTag); else { write(StringTag); - write(str); + write(string); } } - void dumpStringObject(String str) + void dumpStringObject(const String& string) { - if (str.isEmpty()) + if (string.isEmpty()) write(EmptyStringObjectTag); else { write(StringObjectTag); - write(str); + write(string); } } bool dumpArrayBufferView(JSObject* obj, SerializationReturnCode& code) { + VM& vm = m_exec->vm(); write(ArrayBufferViewTag); - if (obj->inherits(JSDataView::info())) + if (obj->inherits(vm, JSDataView::info())) write(DataViewTag); - else if (obj->inherits(JSUint8ClampedArray::info())) + else if (obj->inherits(vm, JSUint8ClampedArray::info())) write(Uint8ClampedArrayTag); - else if (obj->inherits(JSInt8Array::info())) + else if (obj->inherits(vm, JSInt8Array::info())) write(Int8ArrayTag); - else if (obj->inherits(JSUint8Array::info())) + else if (obj->inherits(vm, JSUint8Array::info())) write(Uint8ArrayTag); - else if (obj->inherits(JSInt16Array::info())) + else if (obj->inherits(vm, JSInt16Array::info())) write(Int16ArrayTag); - else if (obj->inherits(JSUint16Array::info())) + else if (obj->inherits(vm, JSUint16Array::info())) write(Uint16ArrayTag); - else if (obj->inherits(JSInt32Array::info())) + else if (obj->inherits(vm, JSInt32Array::info())) write(Int32ArrayTag); - else if (obj->inherits(JSUint32Array::info())) + else if (obj->inherits(vm, JSUint32Array::info())) write(Uint32ArrayTag); - else if (obj->inherits(JSFloat32Array::info())) + else if (obj->inherits(vm, JSFloat32Array::info())) write(Float32ArrayTag); - else if (obj->inherits(JSFloat64Array::info())) + else if (obj->inherits(vm, JSFloat64Array::info())) write(Float64ArrayTag); else return false; - RefPtr<ArrayBufferView> arrayBufferView = toArrayBufferView(obj); + RefPtr<ArrayBufferView> arrayBufferView = toPossiblySharedArrayBufferView(vm, obj); write(static_cast<uint32_t>(arrayBufferView->byteOffset())); write(static_cast<uint32_t>(arrayBufferView->byteLength())); - RefPtr<ArrayBuffer> arrayBuffer = arrayBufferView->buffer(); + RefPtr<ArrayBuffer> arrayBuffer = arrayBufferView->possiblySharedBuffer(); if (!arrayBuffer) { - code = ValidationError; + code = SerializationReturnCode::ValidationError; return true; } JSValue bufferObj = toJS(m_exec, jsCast<JSDOMGlobalObject*>(m_exec->lexicalGlobalObject()), arrayBuffer.get()); @@ -704,8 +753,7 @@ private: } if (value.isString()) { - String str = asString(value)->value(m_exec); - dumpString(str); + dumpString(asString(value)->value(m_exec)); return true; } @@ -715,31 +763,32 @@ private: return true; } - if (value.isObject() && asObject(value)->inherits(DateInstance::info())) { + VM& vm = m_exec->vm(); + if (value.isObject() && asObject(value)->inherits(vm, DateInstance::info())) { write(DateTag); write(asDateInstance(value)->internalNumber()); return true; } - if (isArray(value)) + if (isArray(vm, value)) return false; if (value.isObject()) { JSObject* obj = asObject(value); - if (obj->inherits(BooleanObject::info())) { + if (obj->inherits(vm, 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 (obj->inherits(vm, 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 (obj->inherits(vm, NumberObject::info())) { if (!startObjectInternal(obj)) // handle duplicates return true; write(NumberObjectTag); @@ -747,12 +796,12 @@ private: write(obj->internalValue().asNumber()); return true; } - if (File* file = toFile(obj)) { + if (File* file = JSFile::toWrapped(vm, obj)) { write(FileTag); write(file); return true; } - if (FileList* list = toFileList(obj)) { + if (FileList* list = JSFileList::toWrapped(vm, obj)) { write(FileListTag); unsigned length = list->length(); write(length); @@ -760,7 +809,7 @@ private: write(list->item(i)); return true; } - if (Blob* blob = toBlob(obj)) { + if (Blob* blob = JSBlob::toWrapped(vm, obj)) { write(BlobTag); m_blobURLs.append(blob->url()); write(blob->url()); @@ -768,7 +817,7 @@ private: write(blob->size()); return true; } - if (ImageData* data = toImageData(obj)) { + if (ImageData* data = JSImageData::toWrapped(vm, obj)) { write(ImageDataTag); write(data->width()); write(data->height()); @@ -776,7 +825,7 @@ private: write(data->data()->data(), data->data()->length()); return true; } - if (obj->inherits(RegExpObject::info())) { + if (obj->inherits(vm, RegExpObject::info())) { RegExpObject* regExp = asRegExpObject(obj); char flags[3]; int flagCount = 0; @@ -791,7 +840,7 @@ private: write(String(flags, flagCount)); return true; } - if (obj->inherits(JSMessagePort::info())) { + if (obj->inherits(vm, JSMessagePort::info())) { ObjectPool::iterator index = m_transferredMessagePorts.find(obj); if (index != m_transferredMessagePorts.end()) { write(MessagePortReferenceTag); @@ -799,12 +848,12 @@ private: return true; } // MessagePort object could not be found in transferred message ports - code = ValidationError; + code = SerializationReturnCode::ValidationError; return true; } - if (ArrayBuffer* arrayBuffer = toArrayBuffer(obj)) { + if (ArrayBuffer* arrayBuffer = toPossiblySharedArrayBuffer(vm, obj)) { if (arrayBuffer->isNeutered()) { - code = ValidationError; + code = SerializationReturnCode::ValidationError; return true; } ObjectPool::iterator index = m_transferredArrayBuffers.find(obj); @@ -815,12 +864,25 @@ private: } if (!startObjectInternal(obj)) // handle duplicates return true; + + if (arrayBuffer->isShared() + && m_context == SerializationContext::WorkerPostMessage) { + uint32_t index = m_sharedBuffers.size(); + ArrayBufferContents contents; + if (arrayBuffer->shareWith(contents)) { + write(SharedArrayBufferTag); + m_sharedBuffers.append(WTFMove(contents)); + write(index); + 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 (obj->inherits(vm, JSArrayBufferView::info())) { if (checkForDuplicate(obj)) return true; bool success = dumpArrayBufferView(obj, code); @@ -828,9 +890,19 @@ private: return success; } #if ENABLE(SUBTLE_CRYPTO) - if (CryptoKey* key = toCryptoKey(obj)) { + if (CryptoKey* key = JSCryptoKey::toWrapped(vm, obj)) { write(CryptoKeyTag); - write(key); + Vector<uint8_t> serializedKey; + Vector<String> dummyBlobURLs; + Vector<RefPtr<MessagePort>> dummyMessagePorts; + Vector<RefPtr<JSC::ArrayBuffer>> dummyArrayBuffers; + ArrayBufferContentsArray dummySharedBuffers; + CloneSerializer rawKeySerializer(m_exec, dummyMessagePorts, dummyArrayBuffers, dummyBlobURLs, serializedKey, SerializationContext::Default, dummySharedBuffers); + rawKeySerializer.write(key); + Vector<uint8_t> wrappedKey; + if (!wrapCryptoKey(m_exec, serializedKey, wrappedKey)) + return false; + write(wrappedKey); return true; } #endif @@ -921,7 +993,7 @@ private: template <class T> void writeConstantPoolIndex(const T& constantPool, unsigned i) { - ASSERT(static_cast<int32_t>(i) < constantPool.size()); + ASSERT(i < constantPool.size()); if (constantPool.size() <= 0xFF) write(static_cast<uint8_t>(i)); else if (constantPool.size() <= 0xFFFF) @@ -933,28 +1005,34 @@ private: void write(const Identifier& ident) { const String& str = ident.string(); - StringConstantPool::AddResult addResult = m_constantPool.add(str.impl(), m_constantPool.size()); + StringConstantPool::AddResult addResult = m_constantPool.add(ident.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; - } + unsigned length = str.length(); // Guard against overflow - if (str.length() > (std::numeric_limits<uint32_t>::max() - sizeof(uint32_t)) / sizeof(UChar)) { + if (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())) + if (str.is8Bit()) + writeLittleEndian<uint32_t>(m_buffer, length | StringDataIs8BitFlag); + else + writeLittleEndian<uint32_t>(m_buffer, length); + + if (!length) + return; + if (str.is8Bit()) { + if (!writeLittleEndian(m_buffer, str.characters8(), length)) + fail(); + return; + } + if (!writeLittleEndian(m_buffer, str.characters16(), length)) fail(); } @@ -963,7 +1041,7 @@ private: if (str.isNull()) write(m_emptyIdentifier); else - write(Identifier(m_exec, str)); + write(Identifier::fromString(m_exec, str)); } void write(const Vector<uint8_t>& vector) @@ -979,6 +1057,7 @@ private: write(file->path()); write(file->url()); write(file->type()); + write(file->name()); } #if ENABLE(SUBTLE_CRYPTO) @@ -1095,9 +1174,11 @@ private: void write(const CryptoKey* key) { + write(currentKeyFormatVersion); + write(key->extractable()); - CryptoKeyUsage usages = key->usagesBitmap(); + CryptoKeyUsageBitmap usages = key->usagesBitmap(); write(countUsages(usages)); if (usages & CryptoKeyUsageEncrypt) write(CryptoKeyUsageTag::Encrypt); @@ -1119,23 +1200,23 @@ private: switch (key->keyClass()) { case CryptoKeyClass::HMAC: write(CryptoKeyClassSubtag::HMAC); - write(toCryptoKeyHMAC(key)->key()); - write(toCryptoKeyHMAC(key)->hashAlgorithmIdentifier()); + write(downcast<CryptoKeyHMAC>(*key).key()); + write(downcast<CryptoKeyHMAC>(*key).hashAlgorithmIdentifier()); break; case CryptoKeyClass::AES: write(CryptoKeyClassSubtag::AES); write(key->algorithmIdentifier()); - write(toCryptoKeyAES(key)->key()); + write(downcast<CryptoKeyAES>(*key).key()); break; case CryptoKeyClass::RSA: write(CryptoKeyClassSubtag::RSA); write(key->algorithmIdentifier()); CryptoAlgorithmIdentifier hash; - bool isRestrictedToHash = toCryptoKeyRSA(key)->isRestrictedToHash(hash); + bool isRestrictedToHash = downcast<CryptoKeyRSA>(*key).isRestrictedToHash(hash); write(isRestrictedToHash); if (isRestrictedToHash) write(hash); - write(toCryptoKeyDataRSAComponents(*key->exportData())); + write(downcast<CryptoKeyDataRSAComponents>(*key->exportData())); break; } } @@ -1151,20 +1232,23 @@ private: ObjectPool m_objectPool; ObjectPool m_transferredMessagePorts; ObjectPool m_transferredArrayBuffers; - typedef HashMap<RefPtr<StringImpl>, uint32_t, IdentifierRepHash> StringConstantPool; + typedef HashMap<RefPtr<UniquedStringImpl>, uint32_t, IdentifierRepHash> StringConstantPool; StringConstantPool m_constantPool; Identifier m_emptyIdentifier; + SerializationContext m_context; + ArrayBufferContentsArray& m_sharedBuffers; }; SerializationReturnCode CloneSerializer::serialize(JSValue in) { + VM& vm = m_exec->vm(); 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<JSValue, 4> iteratorValueStack; + Vector<JSMapIterator*, 4> mapIteratorStack; + Vector<JSSetIterator*, 4> setIteratorStack; + Vector<JSValue, 4> mapIteratorValueStack; Vector<WalkerState, 16> stateStack; WalkerState state = StateUnknown; JSValue inValue = in; @@ -1172,9 +1256,9 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) switch (state) { arrayStartState: case ArrayStartState: { - ASSERT(isArray(inValue)); + ASSERT(isArray(vm, inValue)); if (inputObjectStack.size() > maximumFilterRecursion) - return StackOverflowError; + return SerializationReturnCode::StackOverflowError; JSArray* inArray = asArray(inValue); unsigned length = inArray->length(); @@ -1193,8 +1277,8 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) indexStack.removeLast(); lengthStack.removeLast(); - propertyStack.append(PropertyNameArray(m_exec)); - array->methodTable()->getOwnNonIndexPropertyNames(array, m_exec, propertyStack.last(), ExcludeDontEnumProperties); + propertyStack.append(PropertyNameArray(m_exec, PropertyNameMode::Strings)); + array->methodTable()->getOwnNonIndexPropertyNames(array, m_exec, propertyStack.last(), EnumerationMode()); if (propertyStack.last().size()) { write(NonIndexPropertiesTag); indexStack.append(0); @@ -1213,9 +1297,9 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) } write(index); - SerializationReturnCode terminalCode = SuccessfullyCompleted; + auto terminalCode = SerializationReturnCode::SuccessfullyCompleted; if (dumpIfTerminal(inValue, terminalCode)) { - if (terminalCode != SuccessfullyCompleted) + if (terminalCode != SerializationReturnCode::SuccessfullyCompleted) return terminalCode; indexStack.last()++; goto arrayStartVisitMember; @@ -1231,7 +1315,7 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) case ObjectStartState: { ASSERT(inValue.isObject()); if (inputObjectStack.size() > maximumFilterRecursion) - return StackOverflowError; + return SerializationReturnCode::StackOverflowError; JSObject* inObject = asObject(inValue); if (!startObject(inObject)) break; @@ -1239,12 +1323,12 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) // 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; + if (inObject->classInfo(vm) != JSFinalObject::info()) + return SerializationReturnCode::DataCloneError; inputObjectStack.append(inObject); indexStack.append(0); - propertyStack.append(PropertyNameArray(m_exec)); - inObject->methodTable()->getOwnPropertyNames(inObject, m_exec, propertyStack.last(), ExcludeDontEnumProperties); + propertyStack.append(PropertyNameArray(m_exec, PropertyNameMode::Strings)); + inObject->methodTable()->getOwnPropertyNames(inObject, m_exec, propertyStack.last(), EnumerationMode()); } objectStartVisitMember: FALLTHROUGH; @@ -1261,7 +1345,7 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) } inValue = getProperty(object, properties[index]); if (shouldTerminate()) - return ExistingExceptionError; + return SerializationReturnCode::ExistingExceptionError; if (!inValue) { // Property was removed during serialisation @@ -1271,20 +1355,20 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) write(properties[index]); if (shouldTerminate()) - return ExistingExceptionError; + return SerializationReturnCode::ExistingExceptionError; - SerializationReturnCode terminalCode = SuccessfullyCompleted; + auto terminalCode = SerializationReturnCode::SuccessfullyCompleted; if (!dumpIfTerminal(inValue, terminalCode)) { stateStack.append(ObjectEndVisitMember); goto stateUnknown; } - if (terminalCode != SuccessfullyCompleted) + if (terminalCode != SerializationReturnCode::SuccessfullyCompleted) return terminalCode; FALLTHROUGH; } case ObjectEndVisitMember: { if (shouldTerminate()) - return ExistingExceptionError; + return SerializationReturnCode::ExistingExceptionError; indexStack.last()++; goto objectStartVisitMember; @@ -1292,78 +1376,97 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) mapStartState: { ASSERT(inValue.isObject()); if (inputObjectStack.size() > maximumFilterRecursion) - return StackOverflowError; + return SerializationReturnCode::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()); + JSMapIterator* iterator = JSMapIterator::create(vm, m_exec->lexicalGlobalObject()->mapIteratorStructure(), inMap, IterateKeyValue); + m_gcBuffer.append(inMap); + m_gcBuffer.append(iterator); + mapIteratorStack.append(iterator); 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(); + JSMapIterator* iterator = mapIteratorStack.last(); + JSValue key, value; + if (!iterator->nextKeyValue(m_exec, key, value)) { + mapIteratorStack.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); + ASSERT(jsDynamicDowncast<JSMap*>(vm, object)); + propertyStack.append(PropertyNameArray(m_exec, PropertyNameMode::Strings)); + object->methodTable()->getOwnPropertyNames(object, m_exec, propertyStack.last(), EnumerationMode()); write(NonMapPropertiesTag); indexStack.append(0); goto objectStartVisitMember; } - inValue = ptr.key(); - m_gcBuffer.append(ptr.value()); - iteratorValueStack.append(ptr.value()); + inValue = key; + m_gcBuffer.append(value); + mapIteratorValueStack.append(value); stateStack.append(MapDataEndVisitKey); goto stateUnknown; } case MapDataEndVisitKey: { - inValue = iteratorValueStack.last(); - iteratorValueStack.removeLast(); + inValue = mapIteratorValueStack.last(); + mapIteratorValueStack.removeLast(); stateStack.append(MapDataEndVisitValue); goto stateUnknown; } case MapDataEndVisitValue: { - if (iteratorStack.last() != mapDataStack.last()->end()) - ++iteratorStack.last(); goto mapDataStartVisitEntry; } + setStartState: { + ASSERT(inValue.isObject()); + if (inputObjectStack.size() > maximumFilterRecursion) + return SerializationReturnCode::StackOverflowError; + JSSet* inSet = jsCast<JSSet*>(inValue); + if (!startSet(inSet)) + break; + JSSetIterator* iterator = JSSetIterator::create(vm, m_exec->lexicalGlobalObject()->setIteratorStructure(), inSet, IterateKey); + m_gcBuffer.append(inSet); + m_gcBuffer.append(iterator); + setIteratorStack.append(iterator); + inputObjectStack.append(inSet); + goto setDataStartVisitEntry; + } + setDataStartVisitEntry: + case SetDataStartVisitEntry: { + JSSetIterator* iterator = setIteratorStack.last(); + JSValue key; + if (!iterator->next(m_exec, key)) { + setIteratorStack.removeLast(); + JSObject* object = inputObjectStack.last(); + ASSERT(jsDynamicDowncast<JSSet*>(vm, object)); + propertyStack.append(PropertyNameArray(m_exec, PropertyNameMode::Strings)); + object->methodTable()->getOwnPropertyNames(object, m_exec, propertyStack.last(), EnumerationMode()); + write(NonSetPropertiesTag); + indexStack.append(0); + goto objectStartVisitMember; + } + inValue = key; + stateStack.append(SetDataEndVisitKey); + goto stateUnknown; + } + case SetDataEndVisitKey: { + goto setDataStartVisitEntry; + } + stateUnknown: case StateUnknown: { - SerializationReturnCode terminalCode = SuccessfullyCompleted; + auto terminalCode = SerializationReturnCode::SuccessfullyCompleted; if (dumpIfTerminal(inValue, terminalCode)) { - if (terminalCode != SuccessfullyCompleted) + if (terminalCode != SerializationReturnCode::SuccessfullyCompleted) return terminalCode; break; } - if (isArray(inValue)) + if (isArray(vm, inValue)) goto arrayStartState; - if (isMap(inValue)) + if (isMap(vm, inValue)) goto mapStartState; - if (isSet(inValue)) + if (isSet(vm, inValue)) goto setStartState; goto objectStartState; } @@ -1375,13 +1478,11 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) stateStack.removeLast(); } if (m_failed) - return UnspecifiedError; + return SerializationReturnCode::UnspecifiedError; - return SuccessfullyCompleted; + return SerializationReturnCode::SuccessfullyCompleted; } -typedef Vector<JSC::ArrayBufferContents> ArrayBufferContentsArray; - class CloneDeserializer : CloneBase { public: static String deserializeString(const Vector<uint8_t>& buffer) @@ -1397,23 +1498,23 @@ public: if (!readLittleEndian(ptr, end, tag) || tag != StringTag) return String(); uint32_t length; - if (!readLittleEndian(ptr, end, length) || length >= StringPoolTag) + if (!readLittleEndian(ptr, end, length)) return String(); + bool is8Bit = length & StringDataIs8BitFlag; + length &= ~StringDataIs8BitFlag; String str; - if (!readString(ptr, end, str, length)) + if (!readString(ptr, end, str, length, is8Bit)) return String(); - return String(str.impl()); + return str; } - static DeserializationResult deserialize(ExecState* exec, JSGlobalObject* globalObject, - MessagePortArray* messagePorts, ArrayBufferContentsArray* arrayBufferContentsArray, - const Vector<uint8_t>& buffer) + static DeserializationResult deserialize(ExecState* exec, JSGlobalObject* globalObject, Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContentsArray, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers) { if (!buffer.size()) - return std::make_pair(jsNull(), UnspecifiedError); - CloneDeserializer deserializer(exec, globalObject, messagePorts, arrayBufferContentsArray, buffer); + return std::make_pair(jsNull(), SerializationReturnCode::UnspecifiedError); + CloneDeserializer deserializer(exec, globalObject, messagePorts, arrayBufferContentsArray, buffer, blobURLs, blobFilePaths, sharedBuffers); if (!deserializer.isValid()) - return std::make_pair(JSValue(), ValidationError); + return std::make_pair(JSValue(), SerializationReturnCode::ValidationError); return deserializer.deserialize(); } @@ -1456,12 +1557,10 @@ private: size_t m_index; }; - CloneDeserializer(ExecState* exec, JSGlobalObject* globalObject, - MessagePortArray* messagePorts, ArrayBufferContentsArray* arrayBufferContents, - const Vector<uint8_t>& buffer) + CloneDeserializer(ExecState* exec, JSGlobalObject* globalObject, Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, const Vector<uint8_t>& buffer) : CloneBase(exec) , m_globalObject(globalObject) - , m_isDOMGlobalObject(globalObject->inherits(JSDOMGlobalObject::info())) + , m_isDOMGlobalObject(globalObject->inherits(globalObject->vm(), JSDOMGlobalObject::info())) , m_ptr(buffer.data()) , m_end(buffer.data() + buffer.size()) , m_version(0xFFFFFFFF) @@ -1473,13 +1572,26 @@ private: m_version = 0xFFFFFFFF; } - DeserializationResult deserialize(); - - void throwValidationError() + CloneDeserializer(ExecState* exec, JSGlobalObject* globalObject, Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers) + : CloneBase(exec) + , m_globalObject(globalObject) + , m_isDOMGlobalObject(globalObject->inherits(globalObject->vm(), 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) + , m_blobURLs(blobURLs) + , m_blobFilePaths(blobFilePaths) + , m_sharedBuffers(sharedBuffers) { - m_exec->vm().throwException(m_exec, createTypeError(m_exec, "Unable to deserialize data.")); + if (!read(m_version)) + m_version = 0xFFFFFFFF; } + DeserializationResult deserialize(); + bool isValid() const { return m_version <= CurrentVersion; } template <typename T> bool readLittleEndian(T& value) @@ -1582,11 +1694,19 @@ private: return read(i); } - static bool readString(const uint8_t*& ptr, const uint8_t* end, String& str, unsigned length) + static bool readString(const uint8_t*& ptr, const uint8_t* end, String& str, unsigned length, bool is8Bit) { if (length >= std::numeric_limits<int32_t>::max() / sizeof(UChar)) return false; + if (is8Bit) { + if ((end - ptr) < static_cast<int>(length)) + return false; + str = String(reinterpret_cast<const LChar*>(ptr), length); + ptr += length; + return true; + } + unsigned size = length * sizeof(UChar); if ((end - ptr) < static_cast<int>(size)) return false; @@ -1602,7 +1722,7 @@ private: readLittleEndian(ptr, end, ch); buffer.append(ch); } - str = String::adopt(buffer); + str = String::adopt(WTFMove(buffer)); #endif return true; } @@ -1637,8 +1757,10 @@ private: cachedString = CachedStringRef(&m_constantPool, index); return true; } + bool is8Bit = length & StringDataIs8BitFlag; + length &= ~StringDataIs8BitFlag; String str; - if (!readString(m_ptr, m_end, str, length)) { + if (!readString(m_ptr, m_end, str, length, is8Bit)) { fail(); return false; } @@ -1683,8 +1805,17 @@ private: CachedStringRef type; if (!readStringData(type)) return 0; + CachedStringRef name; + if (!readStringData(name)) + return 0; + + // If the blob URL for this file has an associated blob file path, prefer that one over the "built-in" path. + String filePath = blobFilePathForBlobURL(url->string()); + if (filePath.isEmpty()) + filePath = path->string(); + if (m_isDOMGlobalObject) - file = File::create(path->string(), URL(URL(), url->string()), type->string()); + file = File::deserialize(filePath, URL(URL(), url->string()), type->string(), name->string()); return true; } @@ -1700,7 +1831,7 @@ private: return true; } - bool readArrayBufferView(JSValue& arrayBufferView) + bool readArrayBufferView(VM& vm, JSValue& arrayBufferView) { ArrayBufferViewSubtag arrayBufferViewSubtag; if (!readArrayBufferViewSubtag(arrayBufferViewSubtag)) @@ -1712,7 +1843,7 @@ private: if (!read(byteLength)) return false; JSObject* arrayBufferObj = asObject(readTerminal()); - if (!arrayBufferObj || !arrayBufferObj->inherits(JSArrayBuffer::info())) + if (!arrayBufferObj || !arrayBufferObj->inherits(vm, JSArrayBuffer::info())) return false; unsigned elementSize = typedArrayElementSize(arrayBufferViewSubtag); @@ -1722,37 +1853,37 @@ private: if (length * elementSize != byteLength) return false; - RefPtr<ArrayBuffer> arrayBuffer = toArrayBuffer(arrayBufferObj); + RefPtr<ArrayBuffer> arrayBuffer = toPossiblySharedArrayBuffer(vm, arrayBufferObj); switch (arrayBufferViewSubtag) { case DataViewTag: - arrayBufferView = getJSValue(DataView::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = getJSValue(DataView::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Int8ArrayTag: - arrayBufferView = getJSValue(Int8Array::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Int8Array::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Uint8ArrayTag: - arrayBufferView = getJSValue(Uint8Array::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Uint8Array::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Uint8ClampedArrayTag: - arrayBufferView = getJSValue(Uint8ClampedArray::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Uint8ClampedArray::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Int16ArrayTag: - arrayBufferView = getJSValue(Int16Array::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Int16Array::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Uint16ArrayTag: - arrayBufferView = getJSValue(Uint16Array::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Uint16Array::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Int32ArrayTag: - arrayBufferView = getJSValue(Int32Array::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Int32Array::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Uint32ArrayTag: - arrayBufferView = getJSValue(Uint32Array::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Uint32Array::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Float32ArrayTag: - arrayBufferView = getJSValue(Float32Array::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Float32Array::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; case Float64ArrayTag: - arrayBufferView = getJSValue(Float64Array::create(arrayBuffer, byteOffset, length).get()); + arrayBufferView = toJS(m_exec, m_globalObject, Float64Array::create(WTFMove(arrayBuffer), byteOffset, length).get()); return true; default: return false; @@ -1884,7 +2015,7 @@ private: return true; } - bool readHMACKey(bool extractable, CryptoKeyUsage usages, RefPtr<CryptoKey>& result) + bool readHMACKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result) { Vector<uint8_t> keyData; if (!read(keyData)) @@ -1896,7 +2027,7 @@ private: return true; } - bool readAESKey(bool extractable, CryptoKeyUsage usages, RefPtr<CryptoKey>& result) + bool readAESKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result) { CryptoAlgorithmIdentifier algorithm; if (!read(algorithm)) @@ -1910,7 +2041,7 @@ private: return true; } - bool readRSAKey(bool extractable, CryptoKeyUsage usages, RefPtr<CryptoKey>& result) + bool readRSAKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result) { CryptoAlgorithmIdentifier algorithm; if (!read(algorithm)) @@ -1936,10 +2067,8 @@ private: 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); + auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages); + result = WTFMove(key); return true; } @@ -1953,10 +2082,8 @@ private: 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); + auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages); + result = WTFMove(key); return true; } @@ -1987,15 +2114,17 @@ private: } 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); + auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages); + result = WTFMove(key); return true; } bool readCryptoKey(JSValue& cryptoKey) { + uint32_t keyFormatVersion; + if (!read(keyFormatVersion) || keyFormatVersion > currentKeyFormatVersion) + return false; + int32_t extractable; if (!read(extractable)) return false; @@ -2004,7 +2133,7 @@ private: if (!read(usagesCount)) return false; - CryptoKeyUsage usages = 0; + CryptoKeyUsageBitmap usages = 0; for (uint32_t i = 0; i < usagesCount; ++i) { CryptoKeyUsageTag usage; if (!read(usage)) @@ -2066,6 +2195,12 @@ private: return toJS(m_exec, jsCast<JSDOMGlobalObject*>(m_globalObject), nativeObj); } + template<class T> + JSValue getJSValue(T& nativeObj) + { + return getJSValue(&nativeObj); + } + JSValue readTerminal() { SerializationTag tag = readTag(); @@ -2132,29 +2267,29 @@ private: unsigned length = 0; if (!read(length)) return JSValue(); - RefPtr<FileList> result = FileList::create(); + Vector<RefPtr<File>> files; for (unsigned i = 0; i < length; i++) { RefPtr<File> file; if (!readFile(file)) return JSValue(); if (m_isDOMGlobalObject) - result->append(file.get()); + files.append(WTFMove(file)); } if (!m_isDOMGlobalObject) return jsNull(); - return getJSValue(result.get()); + return getJSValue(FileList::create(WTFMove(files)).get()); } case ImageDataTag: { - int32_t width; + uint32_t width; if (!read(width)) return JSValue(); - int32_t height; + uint32_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) { + if (static_cast<uint32_t>(m_end - m_ptr) < length) { fail(); return JSValue(); } @@ -2162,8 +2297,17 @@ private: m_ptr += length; return jsNull(); } - RefPtr<ImageData> result = ImageData::create(IntSize(width, height)); - memcpy(result->data()->data(), m_ptr, length); + IntSize imageSize(width, height); + RELEASE_ASSERT(!length || (imageSize.area() * 4).unsafeGet() <= length); + RefPtr<ImageData> result = ImageData::create(imageSize); + if (!result) { + fail(); + return JSValue(); + } + if (length) + memcpy(result->data()->data(), m_ptr, length); + else + result->data()->zeroFill(); m_ptr += length; return getJSValue(result.get()); } @@ -2179,7 +2323,7 @@ private: return JSValue(); if (!m_isDOMGlobalObject) return jsNull(); - return getJSValue(Blob::create(URL(URL(), url->string()), type->string(), size).get()); + return getJSValue(Blob::deserialize(URL(URL(), url->string()), type->string(), size, blobFilePathForBlobURL(url->string())).get()); } case StringTag: { CachedStringRef cachedString; @@ -2227,11 +2371,11 @@ private: case MessagePortReferenceTag: { uint32_t index; bool indexSuccessfullyRead = read(index); - if (!indexSuccessfullyRead || !m_messagePorts || index >= m_messagePorts->size()) { + if (!indexSuccessfullyRead || index >= m_messagePorts.size()) { fail(); return JSValue(); } - return getJSValue(m_messagePorts->at(index).get()); + return getJSValue(m_messagePorts[index].get()); } case ArrayBufferTag: { RefPtr<ArrayBuffer> arrayBuffer; @@ -2239,7 +2383,14 @@ private: fail(); return JSValue(); } - JSValue result = getJSValue(arrayBuffer.get()); + Structure* structure = m_globalObject->arrayBufferStructure(arrayBuffer->sharingMode()); + // A crazy RuntimeFlags mismatch could mean that we are not equipped to handle shared + // array buffers while the sender is. In that case, we would see a null structure here. + if (!structure) { + fail(); + return JSValue(); + } + JSValue result = JSArrayBuffer::create(m_exec->vm(), structure, WTFMove(arrayBuffer)); m_gcBuffer.append(result); return result; } @@ -2252,13 +2403,27 @@ private: } if (!m_arrayBuffers[index]) - m_arrayBuffers[index] = ArrayBuffer::create(m_arrayBufferContents->at(index)); + m_arrayBuffers[index] = ArrayBuffer::create(WTFMove(m_arrayBufferContents->at(index))); return getJSValue(m_arrayBuffers[index].get()); } + case SharedArrayBufferTag: { + uint32_t index = UINT_MAX; + bool indexSuccessfullyRead = read(index); + if (!indexSuccessfullyRead || !m_sharedBuffers || index >= m_sharedBuffers->size()) { + fail(); + return JSValue(); + } + + RELEASE_ASSERT(m_sharedBuffers->at(index)); + RefPtr<ArrayBuffer> buffer = ArrayBuffer::create(WTFMove(m_sharedBuffers->at(index))); + JSValue result = getJSValue(buffer.get()); + m_gcBuffer.append(result); + return result; + } case ArrayBufferViewTag: { JSValue arrayBufferView; - if (!readArrayBufferView(arrayBufferView)) { + if (!readArrayBufferView(m_exec->vm(), arrayBufferView)) { fail(); return JSValue(); } @@ -2267,8 +2432,20 @@ private: } #if ENABLE(SUBTLE_CRYPTO) case CryptoKeyTag: { + Vector<uint8_t> wrappedKey; + if (!read(wrappedKey)) { + fail(); + return JSValue(); + } + Vector<uint8_t> serializedKey; + if (!unwrapCryptoKey(m_exec, wrappedKey, serializedKey)) { + fail(); + return JSValue(); + } JSValue cryptoKey; - if (!readCryptoKey(cryptoKey)) { + Vector<RefPtr<MessagePort>> dummyMessagePorts; + CloneDeserializer rawKeyDeserializer(m_exec, m_globalObject, dummyMessagePorts, nullptr, serializedKey); + if (!rawKeyDeserializer.readCryptoKey(cryptoKey)) { fail(); return JSValue(); } @@ -2282,9 +2459,10 @@ private: } } - bool consumeMapDataTerminationIfPossible() + template<SerializationTag Tag> + bool consumeCollectionDataTerminationIfPossible() { - if (readTag() == NonMapPropertiesTag) + if (readTag() == Tag) return true; m_ptr--; return false; @@ -2296,18 +2474,36 @@ private: const uint8_t* m_end; unsigned m_version; Vector<CachedString> m_constantPool; - MessagePortArray* m_messagePorts; + Vector<RefPtr<MessagePort>>& m_messagePorts; ArrayBufferContentsArray* m_arrayBufferContents; - ArrayBufferArray m_arrayBuffers; + Vector<RefPtr<JSC::ArrayBuffer>> m_arrayBuffers; + Vector<String> m_blobURLs; + Vector<String> m_blobFilePaths; + ArrayBufferContentsArray* m_sharedBuffers; + + String blobFilePathForBlobURL(const String& blobURL) + { + size_t i = 0; + for (; i < m_blobURLs.size(); ++i) { + if (m_blobURLs[i] == blobURL) + break; + } + + return i < m_blobURLs.size() ? m_blobFilePaths[i] : String(); + } }; DeserializationResult CloneDeserializer::deserialize() { + VM& vm = m_exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + Vector<uint32_t, 16> indexStack; Vector<Identifier, 16> propertyNameStack; Vector<JSObject*, 32> outputObjectStack; - Vector<JSValue, 4> keyStack; - Vector<MapData*, 4> mapDataStack; + Vector<JSValue, 4> mapKeyStack; + Vector<JSMap*, 4> mapStack; + Vector<JSSet*, 4> setStack; Vector<WalkerState, 16> stateStack; WalkerState state = StateUnknown; JSValue outValue; @@ -2322,6 +2518,8 @@ DeserializationResult CloneDeserializer::deserialize() goto error; } JSArray* outArray = constructEmptyArray(m_exec, 0, m_globalObject, length); + if (UNLIKELY(scope.exception())) + goto error; m_gcBuffer.append(outArray); outputObjectStack.append(outArray); } @@ -2361,7 +2559,7 @@ DeserializationResult CloneDeserializer::deserialize() objectStartState: case ObjectStartState: { if (outputObjectStack.size() > maximumFilterRecursion) - return std::make_pair(JSValue(), StackOverflowError); + return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError); JSObject* outObject = constructEmptyObject(m_exec, m_globalObject->objectPrototype()); m_gcBuffer.append(outObject); outputObjectStack.append(outObject); @@ -2382,11 +2580,11 @@ DeserializationResult CloneDeserializer::deserialize() } if (JSValue terminal = readTerminal()) { - putProperty(outputObjectStack.last(), Identifier(m_exec, cachedString->string()), terminal); + putProperty(outputObjectStack.last(), Identifier::fromString(m_exec, cachedString->string()), terminal); goto objectStartVisitMember; } stateStack.append(ObjectEndVisitMember); - propertyNameStack.append(Identifier(m_exec, cachedString->string())); + propertyNameStack.append(Identifier::fromString(m_exec, cachedString->string())); goto stateUnknown; } case ObjectEndVisitMember: { @@ -2396,45 +2594,61 @@ DeserializationResult CloneDeserializer::deserialize() } mapObjectStartState: { if (outputObjectStack.size() > maximumFilterRecursion) - return std::make_pair(JSValue(), StackOverflowError); - JSMap* map = JSMap::create(m_exec->vm(), m_globalObject->mapStructure()); + return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError); + JSMap* map = JSMap::create(m_exec, m_exec->vm(), m_globalObject->mapStructure()); + if (UNLIKELY(scope.exception())) + goto error; 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); + mapStack.append(map); goto mapDataStartVisitEntry; } mapDataStartVisitEntry: case MapDataStartVisitEntry: { - if (consumeMapDataTerminationIfPossible()) { - mapDataStack.removeLast(); + if (consumeCollectionDataTerminationIfPossible<NonMapPropertiesTag>()) { + mapStack.removeLast(); goto objectStartVisitMember; } stateStack.append(MapDataEndVisitKey); goto stateUnknown; } - case MapDataEndVisitKey: { - keyStack.append(outValue); + mapKeyStack.append(outValue); stateStack.append(MapDataEndVisitValue); goto stateUnknown; } - case MapDataEndVisitValue: { - mapDataStack.last()->set(m_exec, keyStack.last(), outValue); - keyStack.removeLast(); + mapStack.last()->set(m_exec, mapKeyStack.last(), outValue); + mapKeyStack.removeLast(); goto mapDataStartVisitEntry; } + + setObjectStartState: { + if (outputObjectStack.size() > maximumFilterRecursion) + return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError); + JSSet* set = JSSet::create(m_exec, m_exec->vm(), m_globalObject->setStructure()); + if (UNLIKELY(scope.exception())) + goto error; + m_gcBuffer.append(set); + outputObjectStack.append(set); + setStack.append(set); + goto setDataStartVisitEntry; + } + setDataStartVisitEntry: + case SetDataStartVisitEntry: { + if (consumeCollectionDataTerminationIfPossible<NonSetPropertiesTag>()) { + setStack.removeLast(); + goto objectStartVisitMember; + } + stateStack.append(SetDataEndVisitKey); + goto stateUnknown; + } + case SetDataEndVisitKey: { + JSSet* set = setStack.last(); + set->add(m_exec, outValue); + goto setDataStartVisitEntry; + } + stateUnknown: case StateUnknown: if (JSValue terminal = readTerminal()) { @@ -2460,63 +2674,39 @@ DeserializationResult CloneDeserializer::deserialize() } ASSERT(outValue); ASSERT(!m_failed); - return std::make_pair(outValue, SuccessfullyCompleted); + return std::make_pair(outValue, SerializationReturnCode::SuccessfullyCompleted); error: fail(); - return std::make_pair(JSValue(), ValidationError); -} - -void SerializedScriptValue::addBlobURL(const String& string) -{ - m_blobURLs.append(Vector<uint16_t>()); - m_blobURLs.last().reserveCapacity(string.length()); - for (size_t i = 0; i < string.length(); i++) - m_blobURLs.last().append(string.characterAt(i)); - m_blobURLs.last().resize(m_blobURLs.last().size()); + return std::make_pair(JSValue(), SerializationReturnCode::ValidationError); } SerializedScriptValue::~SerializedScriptValue() { } -SerializedScriptValue::SerializedScriptValue(const Vector<uint8_t>& buffer) - : m_data(buffer) +SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer) + : m_data(WTFMove(buffer)) { } -SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>& buffer) +SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, const Vector<String>& blobURLs, std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray, std::unique_ptr<ArrayBufferContentsArray> sharedBufferContentsArray) + : m_data(WTFMove(buffer)) + , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray)) + , m_sharedBufferContentsArray(WTFMove(sharedBufferContentsArray)) { - m_data.swap(buffer); + // Since this SerializedScriptValue is meant to be passed between threads, its String data members + // need to be isolatedCopies so we don't run into thread safety issues for the StringImpls. + m_blobURLs.reserveInitialCapacity(blobURLs.size()); + for (auto& url : blobURLs) + m_blobURLs.uncheckedAppend(url.isolatedCopy()); } -SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>& buffer, Vector<String>& blobURLs) +static ExceptionOr<std::unique_ptr<ArrayBufferContentsArray>> transferArrayBuffers(VM& vm, const Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers) { - m_data.swap(buffer); - for (auto& string : blobURLs) - addBlobURL(string); -} + if (arrayBuffers.isEmpty()) + return nullptr; -SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>& buffer, Vector<String>& blobURLs, PassOwnPtr<ArrayBufferContentsArray> arrayBufferContentsArray) - : m_arrayBufferContentsArray(arrayBufferContentsArray) -{ - m_data.swap(buffer); - for (auto& string : blobURLs) - addBlobURL(string); -} - -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); + auto contents = std::make_unique<ArrayBufferContentsArray>(arrayBuffers.size()); HashSet<JSC::ArrayBuffer*> visited; for (size_t arrayBufferIndex = 0; arrayBufferIndex < arrayBuffers.size(); arrayBufferIndex++) { @@ -2524,207 +2714,255 @@ PassOwnPtr<SerializedScriptValue::ArrayBufferContentsArray> SerializedScriptValu continue; visited.add(arrayBuffers[arrayBufferIndex].get()); - bool result = arrayBuffers[arrayBufferIndex]->transfer(contents->at(arrayBufferIndex)); - if (!result) { - code = ValidationError; - return nullptr; - } + bool result = arrayBuffers[arrayBufferIndex]->transferTo(vm, contents->at(arrayBufferIndex)); + if (!result) + return Exception { TypeError }; + } + + return WTFMove(contents); +} + +static void maybeThrowExceptionIfSerializationFailed(ExecState& state, SerializationReturnCode code) +{ + auto& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + switch (code) { + case SerializationReturnCode::SuccessfullyCompleted: + break; + case SerializationReturnCode::StackOverflowError: + throwException(&state, scope, createStackOverflowError(&state)); + break; + case SerializationReturnCode::ValidationError: + throwTypeError(&state, scope, ASCIILiteral("Unable to deserialize data.")); + break; + case SerializationReturnCode::DataCloneError: + throwDataCloneError(state, scope); + break; + case SerializationReturnCode::ExistingExceptionError: + case SerializationReturnCode::UnspecifiedError: + break; + case SerializationReturnCode::InterruptedExecutionError: + ASSERT_NOT_REACHED(); } - return contents.release(); } +static Exception exceptionForSerializationFailure(SerializationReturnCode code) +{ + ASSERT(code != SerializationReturnCode::SuccessfullyCompleted); + + switch (code) { + case SerializationReturnCode::StackOverflowError: + return Exception { StackOverflowError }; + case SerializationReturnCode::ValidationError: + return Exception { TypeError }; + case SerializationReturnCode::DataCloneError: + return Exception { DATA_CLONE_ERR }; + case SerializationReturnCode::ExistingExceptionError: + return Exception { ExistingExceptionError }; + case SerializationReturnCode::UnspecifiedError: + return Exception { TypeError }; + case SerializationReturnCode::SuccessfullyCompleted: + case SerializationReturnCode::InterruptedExecutionError: + ASSERT_NOT_REACHED(); + return Exception { TypeError }; + } + ASSERT_NOT_REACHED(); + return Exception { TypeError }; +} -PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(ExecState* exec, JSValue value, - MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, - SerializationErrorMode throwExceptions) +RefPtr<SerializedScriptValue> SerializedScriptValue::create(ExecState& exec, JSValue value, SerializationErrorMode throwExceptions) { Vector<uint8_t> buffer; Vector<String> blobURLs; - SerializationReturnCode code = CloneSerializer::serialize(exec, value, messagePorts, arrayBuffers, blobURLs, buffer); - - OwnPtr<ArrayBufferContentsArray> arrayBufferContentsArray; + Vector<RefPtr<MessagePort>> dummyMessagePorts; + Vector<RefPtr<JSC::ArrayBuffer>> dummyArrayBuffers; + ArrayBufferContentsArray dummySharedBuffers; + auto code = CloneSerializer::serialize(&exec, value, dummyMessagePorts, dummyArrayBuffers, blobURLs, buffer, SerializationContext::Default, dummySharedBuffers); - if (arrayBuffers && serializationDidCompleteSuccessfully(code)) - arrayBufferContentsArray = transferArrayBuffers(exec, *arrayBuffers, code); - - if (throwExceptions == Throwing) + if (throwExceptions == SerializationErrorMode::Throwing) maybeThrowExceptionIfSerializationFailed(exec, code); - if (!serializationDidCompleteSuccessfully(code)) - return 0; + if (code != SerializationReturnCode::SuccessfullyCompleted) + return nullptr; - return adoptRef(new SerializedScriptValue(buffer, blobURLs, arrayBufferContentsArray.release())); + return adoptRef(*new SerializedScriptValue(WTFMove(buffer), blobURLs, nullptr, nullptr)); } -PassRefPtr<SerializedScriptValue> SerializedScriptValue::create() +ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(ExecState& state, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, Vector<RefPtr<MessagePort>>& messagePorts, SerializationContext context) { - Vector<uint8_t> buffer; - return adoptRef(new SerializedScriptValue(buffer)); -} + VM& vm = state.vm(); + Vector<RefPtr<JSC::ArrayBuffer>> arrayBuffers; + for (auto& transferable : transferList) { + if (auto arrayBuffer = toPossiblySharedArrayBuffer(vm, transferable.get())) { + if (arrayBuffer->isNeutered()) + return Exception { DATA_CLONE_ERR }; + if (arrayBuffer->isShared()) + return Exception { TypeError }; + arrayBuffers.append(WTFMove(arrayBuffer)); + continue; + } + if (auto port = JSMessagePort::toWrapped(vm, transferable.get())) { + // FIXME: This should check if the port is detached as per https://html.spec.whatwg.org/multipage/infrastructure.html#istransferable. + messagePorts.append(WTFMove(port)); + continue; + } + + return Exception { DATA_CLONE_ERR }; + } -PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(const String& string) -{ Vector<uint8_t> buffer; - if (!CloneSerializer::serialize(string, buffer)) - return 0; - return adoptRef(new SerializedScriptValue(buffer)); -} + Vector<String> blobURLs; + std::unique_ptr<ArrayBufferContentsArray> sharedBuffers = std::make_unique<ArrayBufferContentsArray>(); + auto code = CloneSerializer::serialize(&state, value, messagePorts, arrayBuffers, blobURLs, buffer, context, *sharedBuffers); -#if ENABLE(INDEXED_DATABASE) -PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(JSC::ExecState* exec, JSC::JSValue value) -{ - return SerializedScriptValue::create(exec, value, 0, 0); -} + if (code != SerializationReturnCode::SuccessfullyCompleted) + return exceptionForSerializationFailure(code); -PassRefPtr<SerializedScriptValue> SerializedScriptValue::numberValue(double value) -{ - Vector<uint8_t> buffer; - CloneSerializer::serializeNumber(value, buffer); - return adoptRef(new SerializedScriptValue(buffer)); + auto arrayBufferContentsArray = transferArrayBuffers(vm, arrayBuffers); + if (arrayBufferContentsArray.hasException()) + return arrayBufferContentsArray.releaseException(); + + return adoptRef(*new SerializedScriptValue(WTFMove(buffer), blobURLs, arrayBufferContentsArray.releaseReturnValue(), context == SerializationContext::WorkerPostMessage ? WTFMove(sharedBuffers) : nullptr)); } -JSValue SerializedScriptValue::deserialize(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject) +RefPtr<SerializedScriptValue> SerializedScriptValue::create(StringView string) { - return deserialize(exec, globalObject, 0); + Vector<uint8_t> buffer; + if (!CloneSerializer::serialize(string, buffer)) + return nullptr; + return adoptRef(*new SerializedScriptValue(WTFMove(buffer))); } -#endif -PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, - MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, - JSValueRef* exception) +RefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, JSValueRef* exception) { ExecState* exec = toJS(originContext); - APIEntryShim entryShim(exec); + VM& vm = exec->vm(); + JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + JSValue value = toJS(exec, apiValue); - RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(exec, value, messagePorts, arrayBuffers); - if (exec->hadException()) { + RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(*exec, value); + if (UNLIKELY(scope.exception())) { if (exception) - *exception = toRef(exec, exec->exception()); - exec->clearException(); - return 0; + *exception = toRef(exec, scope.exception()->value()); + scope.clearException(); + return nullptr; } ASSERT(serializedValue); - return serializedValue.release(); + return serializedValue; } -PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, - JSValueRef* exception) +String SerializedScriptValue::toString() { - return create(originContext, apiValue, 0, 0, exception); + return CloneDeserializer::deserializeString(m_data); } -String SerializedScriptValue::toString() +JSValue SerializedScriptValue::deserialize(ExecState& exec, JSGlobalObject* globalObject, SerializationErrorMode throwExceptions) { - return CloneDeserializer::deserializeString(m_data); + Vector<RefPtr<MessagePort>> dummyMessagePorts; + return deserialize(exec, globalObject, dummyMessagePorts, throwExceptions); } -JSValue SerializedScriptValue::deserialize(ExecState* exec, JSGlobalObject* globalObject, - MessagePortArray* messagePorts, SerializationErrorMode throwExceptions) +JSValue SerializedScriptValue::deserialize(ExecState& exec, JSGlobalObject* globalObject, Vector<RefPtr<MessagePort>>& 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; + Vector<String> dummyBlobs; + Vector<String> dummyPaths; + return deserialize(exec, globalObject, messagePorts, dummyBlobs, dummyPaths, throwExceptions); } -#if ENABLE(INSPECTOR) -Deprecated::ScriptValue SerializedScriptValue::deserializeForInspector(JSC::ExecState* scriptState) +JSValue SerializedScriptValue::deserialize(ExecState& exec, JSGlobalObject* globalObject, Vector<RefPtr<MessagePort>>& messagePorts, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode throwExceptions) { - JSValue value = deserialize(scriptState, scriptState->lexicalGlobalObject(), 0); - return Deprecated::ScriptValue(scriptState->vm(), value); + DeserializationResult result = CloneDeserializer::deserialize(&exec, globalObject, messagePorts, m_arrayBufferContentsArray.get(), m_data, blobURLs, blobFilePaths, m_sharedBufferContentsArray.get()); + if (throwExceptions == SerializationErrorMode::Throwing) + maybeThrowExceptionIfSerializationFailed(exec, result.second); + return result.first ? result.first : jsNull(); } -#endif -JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception, MessagePortArray* messagePorts) +JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception) { ExecState* exec = toJS(destinationContext); - APIEntryShim entryShim(exec); - JSValue value = deserialize(exec, exec->lexicalGlobalObject(), messagePorts); - if (exec->hadException()) { + VM& vm = exec->vm(); + JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + + JSValue value = deserialize(*exec, exec->lexicalGlobalObject()); + if (UNLIKELY(scope.exception())) { if (exception) - *exception = toRef(exec, exec->exception()); - exec->clearException(); - return 0; + *exception = toRef(exec, scope.exception()->value()); + scope.clearException(); + return nullptr; } ASSERT(value); return toRef(exec, value); } - -JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception) +Ref<SerializedScriptValue> SerializedScriptValue::nullValue() { - return deserialize(destinationContext, exception, 0); + return adoptRef(*new SerializedScriptValue(Vector<uint8_t>())); } -PassRefPtr<SerializedScriptValue> SerializedScriptValue::nullValue() +uint32_t SerializedScriptValue::wireFormatVersion() { - return SerializedScriptValue::create(); + return CurrentVersion; } -PassRefPtr<SerializedScriptValue> SerializedScriptValue::undefinedValue() +#if ENABLE(INDEXED_DATABASE) +Vector<String> SerializedScriptValue::blobURLsIsolatedCopy() const { - Vector<uint8_t> buffer; - CloneSerializer::serializeUndefined(buffer); - return adoptRef(new SerializedScriptValue(buffer)); -} + Vector<String> result; + result.reserveInitialCapacity(m_blobURLs.size()); + for (auto& url : m_blobURLs) + result.uncheckedAppend(url.isolatedCopy()); -PassRefPtr<SerializedScriptValue> SerializedScriptValue::booleanValue(bool value) -{ - Vector<uint8_t> buffer; - CloneSerializer::serializeBoolean(value, buffer); - return adoptRef(new SerializedScriptValue(buffer)); + return result; } -PassRefPtr<SerializedScriptValue> SerializedScriptValue::serialize(const Deprecated::ScriptValue& value, JSC::ExecState* scriptState, SerializationErrorMode throwExceptions) +void SerializedScriptValue::writeBlobsToDiskForIndexedDB(WTF::Function<void (const IDBValue&)>&& completionHandler) { - return SerializedScriptValue::create(scriptState, value.jsValue(), nullptr, nullptr, throwExceptions); -} + ASSERT(isMainThread()); + ASSERT(hasBlobURLs()); -PassRefPtr<SerializedScriptValue> SerializedScriptValue::serialize(const Deprecated::ScriptValue& value, JSC::ExecState* scriptState, MessagePortArray* messagePorts, ArrayBufferArray* arrayBuffers, bool& didThrow) -{ - RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(scriptState, value.jsValue(), messagePorts, arrayBuffers); - didThrow = scriptState->hadException(); - return serializedValue.release(); -} + RefPtr<SerializedScriptValue> protectedThis(this); + blobRegistry().writeBlobsToTemporaryFiles(m_blobURLs, [completionHandler = WTFMove(completionHandler), this, protectedThis = WTFMove(protectedThis)](auto& blobFilePaths) { + ASSERT(isMainThread()); -Deprecated::ScriptValue SerializedScriptValue::deserialize(JSC::ExecState* scriptState, SerializedScriptValue* value, SerializationErrorMode throwExceptions) -{ - return Deprecated::ScriptValue(scriptState->vm(), value->deserialize(scriptState, scriptState->lexicalGlobalObject(), 0, throwExceptions)); -} + if (blobFilePaths.isEmpty()) { + // We should have successfully written blobs to temporary files. + // If we failed, then we can't successfully store this record. + completionHandler({ }); + return; + } -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(); - } + ASSERT(m_blobURLs.size() == blobFilePaths.size()); + + completionHandler({ *this, m_blobURLs, blobFilePaths }); + }); } -bool SerializedScriptValue::serializationDidCompleteSuccessfully(SerializationReturnCode code) +IDBValue SerializedScriptValue::writeBlobsToDiskForIndexedDBSynchronously() { - return (code == SuccessfullyCompleted); -} + ASSERT(!isMainThread()); -uint32_t SerializedScriptValue::wireFormatVersion() -{ - return CurrentVersion; -} + IDBValue value; + Lock lock; + Condition condition; + lock.lock(); + + RunLoop::main().dispatch([this, conditionPtr = &condition, valuePtr = &value] { + writeBlobsToDiskForIndexedDB([conditionPtr, valuePtr](const IDBValue& result) { + ASSERT(isMainThread()); + valuePtr->setAsIsolatedCopy(result); + + conditionPtr->notifyAll(); + }); + }); + condition.wait(lock); + + return value; } + +#endif // ENABLE(INDEXED_DATABASE) + +} // namespace WebCore |