summaryrefslogtreecommitdiff
path: root/Source/WebCore/bindings/js/SerializedScriptValue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/bindings/js/SerializedScriptValue.cpp')
-rw-r--r--Source/WebCore/bindings/js/SerializedScriptValue.cpp1110
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