mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1264642 - Part 4. Use BufferList to replace raw buffers in StructuredClone. r=baku r=billm r=jorendorff
In JS StructuredClone BufferList<SystemAllocPolicy> is typedef'd to JSStructuredCloneData and use everywhere in gecko that stores structured clone data. This patch changed some raw pointers to UniquePtr<JSStructuredCloneData> and some to stack allocated JSStructuredCloneData for better life time management. Some parameters or methods are deleted because of changing to the new data structure. MessagePortMessage now has the exactly same structure with ClonedMessageData. Maybe in the future they can be consolidated. MozReview-Commit-ID: 1IY9p5eKLgv
This commit is contained in:
parent
3dcef6a932
commit
506dfe6ea3
@ -133,7 +133,9 @@ StructuredCloneCallbacksError(JSContext* aCx,
|
||||
NS_WARNING("Failed to clone data.");
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks gCallbacks = {
|
||||
} // anonymous namespace
|
||||
|
||||
const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
|
||||
StructuredCloneCallbacksRead,
|
||||
StructuredCloneCallbacksWrite,
|
||||
StructuredCloneCallbacksError,
|
||||
@ -142,8 +144,6 @@ const JSStructuredCloneCallbacks gCallbacks = {
|
||||
StructuredCloneCallbacksFreeTransfer
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// StructuredCloneHolderBase class
|
||||
|
||||
StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope)
|
||||
@ -185,9 +185,9 @@ StructuredCloneHolderBase::Write(JSContext* aCx,
|
||||
MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
|
||||
MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
|
||||
|
||||
mBuffer = new JSAutoStructuredCloneBuffer(mStructuredCloneScope, &gCallbacks, this);
|
||||
mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this);
|
||||
|
||||
if (!mBuffer->write(aCx, aValue, aTransfer, &gCallbacks, this)) {
|
||||
if (!mBuffer->write(aCx, aValue, aTransfer, &StructuredCloneHolder::sCallbacks, this)) {
|
||||
mBuffer = nullptr;
|
||||
return false;
|
||||
}
|
||||
@ -202,7 +202,7 @@ StructuredCloneHolderBase::Read(JSContext* aCx,
|
||||
MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
|
||||
MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
|
||||
|
||||
bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this);
|
||||
bool ok = mBuffer->read(aCx, aValue, &StructuredCloneHolder::sCallbacks, this);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -311,20 +311,18 @@ StructuredCloneHolder::Read(nsISupports* aParent,
|
||||
void
|
||||
StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
|
||||
JSContext* aCx,
|
||||
uint64_t* aBuffer,
|
||||
size_t aBufferLength,
|
||||
JSStructuredCloneData& aBuffer,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
ReadFromBuffer(aParent, aCx, aBuffer, aBufferLength,
|
||||
ReadFromBuffer(aParent, aCx, aBuffer,
|
||||
JS_STRUCTURED_CLONE_VERSION, aValue, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
|
||||
JSContext* aCx,
|
||||
uint64_t* aBuffer,
|
||||
size_t aBufferLength,
|
||||
JSStructuredCloneData& aBuffer,
|
||||
uint32_t aAlgorithmVersion,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult& aRv)
|
||||
@ -333,53 +331,18 @@ StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
|
||||
mCreationThread == NS_GetCurrentThread());
|
||||
|
||||
MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
|
||||
MOZ_ASSERT(aBuffer);
|
||||
|
||||
mozilla::AutoRestore<nsISupports*> guard(mParent);
|
||||
mParent = aParent;
|
||||
|
||||
if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength, aAlgorithmVersion,
|
||||
mStructuredCloneScope, aValue, &gCallbacks,
|
||||
if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion,
|
||||
mStructuredCloneScope, aValue, &sCallbacks,
|
||||
this)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneHolder::MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
|
||||
mCreationThread == NS_GetCurrentThread());
|
||||
|
||||
MOZ_ASSERT(mBuffer, "MoveBuffer() cannot be called without a Write().");
|
||||
|
||||
if (NS_WARN_IF(!aArray.SetLength(BufferSize(), mozilla::fallible))) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t* buffer;
|
||||
size_t size;
|
||||
mBuffer->steal(&buffer, &size);
|
||||
mBuffer = nullptr;
|
||||
|
||||
memcpy(aArray.Elements(), buffer, size);
|
||||
js_free(buffer);
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneHolder::FreeBuffer(uint64_t* aBuffer,
|
||||
size_t aBufferLength)
|
||||
{
|
||||
MOZ_ASSERT(!mBuffer, "FreeBuffer() must be called without a Write().");
|
||||
MOZ_ASSERT(aBuffer);
|
||||
MOZ_ASSERT(aBufferLength);
|
||||
|
||||
JS_ClearStructuredClone(aBuffer, aBufferLength, &gCallbacks, this, false);
|
||||
}
|
||||
|
||||
/* static */ JSObject*
|
||||
StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
@ -102,20 +102,14 @@ public:
|
||||
return !!mBuffer;
|
||||
}
|
||||
|
||||
uint64_t* BufferData() const
|
||||
JSStructuredCloneData& BufferData() const
|
||||
{
|
||||
MOZ_ASSERT(mBuffer, "Write() has never been called.");
|
||||
return mBuffer->data();
|
||||
}
|
||||
|
||||
size_t BufferSize() const
|
||||
{
|
||||
MOZ_ASSERT(mBuffer, "Write() has never been called.");
|
||||
return mBuffer->nbytes();
|
||||
}
|
||||
|
||||
protected:
|
||||
nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
|
||||
UniquePtr<JSAutoStructuredCloneBuffer> mBuffer;
|
||||
|
||||
StructuredCloneScope mStructuredCloneScope;
|
||||
|
||||
@ -172,12 +166,6 @@ public:
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult &aRv);
|
||||
|
||||
// Sometimes, when IPC is involved, you must send a buffer after a Write().
|
||||
// This method 'steals' the internal data from this class.
|
||||
// You should free this buffer with StructuredCloneHolder::FreeBuffer().
|
||||
void MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Call this method to know if this object is keeping some DOM object alive.
|
||||
bool HasClonedDOMObjects() const
|
||||
{
|
||||
@ -266,29 +254,25 @@ public:
|
||||
JSStructuredCloneWriter* aWriter,
|
||||
JS::Handle<JSObject*> aObj);
|
||||
|
||||
static const JSStructuredCloneCallbacks sCallbacks;
|
||||
|
||||
protected:
|
||||
// If you receive a buffer from IPC, you can use this method to retrieve a
|
||||
// JS::Value. It can happen that you want to pre-populate the array of Blobs
|
||||
// and/or the PortIdentifiers.
|
||||
void ReadFromBuffer(nsISupports* aParent,
|
||||
JSContext* aCx,
|
||||
uint64_t* aBuffer,
|
||||
size_t aBufferLength,
|
||||
JSStructuredCloneData& aBuffer,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult &aRv);
|
||||
|
||||
void ReadFromBuffer(nsISupports* aParent,
|
||||
JSContext* aCx,
|
||||
uint64_t* aBuffer,
|
||||
size_t aBufferLength,
|
||||
JSStructuredCloneData& aBuffer,
|
||||
uint32_t aAlgorithmVersion,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult &aRv);
|
||||
|
||||
// Use this method to free a buffer generated by MoveToBuffer().
|
||||
void FreeBuffer(uint64_t* aBuffer,
|
||||
size_t aBufferLength);
|
||||
|
||||
bool mSupportsCloning;
|
||||
bool mSupportsTransferring;
|
||||
|
||||
|
@ -276,8 +276,13 @@ BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType*
|
||||
ClonedMessageData& aClonedData)
|
||||
{
|
||||
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
|
||||
buffer.data = aData.Data();
|
||||
buffer.dataLength = aData.DataLength();
|
||||
auto iter = aData.Data().Iter();
|
||||
size_t size = aData.Data().Size();
|
||||
bool success;
|
||||
buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success);
|
||||
if (NS_WARN_IF(!success)) {
|
||||
return false;
|
||||
}
|
||||
aClonedData.identfiers().AppendElements(aData.PortIdentifiers());
|
||||
|
||||
const nsTArray<RefPtr<BlobImpl>>& blobImpls = aData.BlobImpls();
|
||||
@ -325,7 +330,7 @@ UnpackClonedMessageData(const ClonedMessageData& aClonedData,
|
||||
const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aClonedData);
|
||||
const InfallibleTArray<MessagePortIdentifier>& identifiers = aClonedData.identfiers();
|
||||
|
||||
aData.UseExternalData(buffer.data, buffer.dataLength);
|
||||
aData.UseExternalData(buffer.data);
|
||||
|
||||
aData.PortIdentifiers().AppendElements(identifiers);
|
||||
|
||||
|
@ -137,7 +137,11 @@ nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoCString binaryData(reinterpret_cast<char*>(Data()), DataLength());
|
||||
auto iter = Data().Iter();
|
||||
size_t size = Data().Size();
|
||||
nsAutoCString binaryData;
|
||||
binaryData.SetLength(size);
|
||||
Data().ReadBytes(iter, binaryData.BeginWriting(), size);
|
||||
nsAutoCString base64Data;
|
||||
nsresult rv = Base64Encode(binaryData, base64Data);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -152,9 +152,13 @@ public:
|
||||
|
||||
ClonedMessageData message;
|
||||
|
||||
bool success;
|
||||
SerializedStructuredCloneBuffer& buffer = message.data();
|
||||
buffer.data = mData->BufferData();
|
||||
buffer.dataLength = mData->BufferSize();
|
||||
auto iter = mData->BufferData().Iter();
|
||||
buffer.data = mData->BufferData().Borrow<js::SystemAllocPolicy>(iter, mData->BufferData().Size(), &success);
|
||||
if (NS_WARN_IF(!success)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PBackgroundChild* backgroundManager = mActor->Manager();
|
||||
MOZ_ASSERT(backgroundManager);
|
||||
|
@ -91,12 +91,11 @@ BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
|
||||
cloneData.BlobImpls().AppendElements(blobs);
|
||||
|
||||
const SerializedStructuredCloneBuffer& buffer = aData.data();
|
||||
cloneData.UseExternalData(buffer.data, buffer.dataLength);
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JS::Value> value(cx, JS::NullValue());
|
||||
if (buffer.dataLength) {
|
||||
if (buffer.data.Size()) {
|
||||
ErrorResult rv;
|
||||
cloneData.UseExternalData(buffer.data);
|
||||
cloneData.Read(cx, &value, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
|
@ -871,7 +871,7 @@ CommonStructuredCloneReadCallback(JSContext* aCx,
|
||||
void
|
||||
ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
|
||||
{
|
||||
if (aBuffer.data()) {
|
||||
if (!aBuffer.empty()) {
|
||||
aBuffer.clear();
|
||||
}
|
||||
}
|
||||
@ -1042,7 +1042,7 @@ IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo)
|
||||
// This is kind of tricky, we only want to release stuff on the main thread,
|
||||
// but we can end up being called on other threads if we have already been
|
||||
// cleared on the main thread.
|
||||
if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) {
|
||||
if (aReadInfo.mCloneBuffer.empty() && !aReadInfo.mFiles.Length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1063,13 +1063,18 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* data = reinterpret_cast<uint64_t*>(aCloneReadInfo.mData.Elements());
|
||||
char* data = reinterpret_cast<char*>(aCloneReadInfo.mData.Elements());
|
||||
size_t dataLen = aCloneReadInfo.mData.Length();
|
||||
|
||||
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
||||
MOZ_ASSERT(!(dataLen % sizeof(uint64_t)));
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
JSStructuredCloneData buf;
|
||||
if (!buf.WriteBytes(data, dataLen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const JSStructuredCloneCallbacks callbacks = {
|
||||
CommonStructuredCloneReadCallback<ValueDeserializationHelper>,
|
||||
nullptr,
|
||||
@ -1081,7 +1086,7 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
|
||||
|
||||
// FIXME: Consider to use StructuredCloneHolder here and in other
|
||||
// deserializing methods.
|
||||
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
|
||||
if (!JS_ReadStructuredClone(aCx, buf, JS_STRUCTURED_CLONE_VERSION,
|
||||
JS::StructuredCloneScope::SameProcessSameThread,
|
||||
aValue, &callbacks, &aCloneReadInfo)) {
|
||||
return false;
|
||||
@ -1105,22 +1110,24 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
|
||||
}
|
||||
|
||||
size_t dataLen = aCloneReadInfo.mData.Length();
|
||||
char* data = reinterpret_cast<char*>(aCloneReadInfo.mData.Elements());
|
||||
|
||||
uint64_t* data =
|
||||
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
|
||||
aCloneReadInfo.mData.Elements()));
|
||||
|
||||
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
||||
MOZ_ASSERT(!(dataLen % sizeof(uint64_t)));
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
JSStructuredCloneData buf;
|
||||
if (!buf.WriteBytes(data, dataLen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const JSStructuredCloneCallbacks callbacks = {
|
||||
CommonStructuredCloneReadCallback<IndexDeserializationHelper>,
|
||||
nullptr,
|
||||
nullptr
|
||||
};
|
||||
|
||||
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
|
||||
if (!JS_ReadStructuredClone(aCx, buf, JS_STRUCTURED_CLONE_VERSION,
|
||||
JS::StructuredCloneScope::SameProcessSameThread,
|
||||
aValue, &callbacks, &aCloneReadInfo)) {
|
||||
return false;
|
||||
@ -1145,16 +1152,19 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
size_t dataLen = aCloneReadInfo.mData.Length();
|
||||
char* data = reinterpret_cast<char*>(aCloneReadInfo.mData.Elements());
|
||||
|
||||
uint64_t* data =
|
||||
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
|
||||
aCloneReadInfo.mData.Elements()));
|
||||
|
||||
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
||||
MOZ_ASSERT(!(dataLen % sizeof(uint64_t)));
|
||||
|
||||
JSAutoRequest ar(aCx);
|
||||
|
||||
JSStructuredCloneData buf;
|
||||
if (!buf.WriteBytes(data, dataLen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static JSStructuredCloneCallbacks callbacks = {
|
||||
CommonStructuredCloneReadCallback<UpgradeDeserializationHelper>,
|
||||
nullptr,
|
||||
@ -1164,7 +1174,7 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
|
||||
nullptr
|
||||
};
|
||||
|
||||
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
|
||||
if (!JS_ReadStructuredClone(aCx, buf, JS_STRUCTURED_CLONE_VERSION,
|
||||
JS::StructuredCloneScope::SameProcessSameThread,
|
||||
aValue, &callbacks, &aCloneReadInfo)) {
|
||||
return false;
|
||||
@ -1297,15 +1307,21 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
|
||||
}
|
||||
|
||||
FallibleTArray<uint8_t> cloneData;
|
||||
if (NS_WARN_IF(!cloneData.SetLength(cloneWriteInfo.mCloneBuffer.nbytes(),
|
||||
fallible))) {
|
||||
size_t size = cloneWriteInfo.mCloneBuffer.data().Size();
|
||||
|
||||
if (NS_WARN_IF(!cloneData.SetLength(size, fallible))) {
|
||||
aRv = NS_ERROR_OUT_OF_MEMORY;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// XXX Remove this
|
||||
memcpy(cloneData.Elements(), cloneWriteInfo.mCloneBuffer.data(),
|
||||
cloneWriteInfo.mCloneBuffer.nbytes());
|
||||
const char* buf;
|
||||
auto iter = cloneWriteInfo.mCloneBuffer.data().Iter();
|
||||
cloneWriteInfo.mCloneBuffer.data().FlattenBytes(iter, &buf, size);
|
||||
|
||||
// FIXME Bug XXXXXX Change SerializedStructuredCloneReadInfo and
|
||||
// SerializedStructuredCloneWriteInfo to use JSStructuredCloneData
|
||||
// instead of raw buffer.
|
||||
memcpy(cloneData.Elements(), buf, size);
|
||||
|
||||
cloneWriteInfo.mCloneBuffer.clear();
|
||||
|
||||
|
@ -47,8 +47,6 @@ StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const
|
||||
inline
|
||||
StructuredCloneReadInfo::StructuredCloneReadInfo()
|
||||
: mDatabase(nullptr)
|
||||
, mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
|
||||
nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
|
||||
}
|
||||
@ -58,8 +56,6 @@ StructuredCloneReadInfo::StructuredCloneReadInfo(
|
||||
SerializedStructuredCloneReadInfo&& aCloneReadInfo)
|
||||
: mData(Move(aCloneReadInfo.data()))
|
||||
, mDatabase(nullptr)
|
||||
, mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
|
||||
nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace ipc {
|
||||
bool
|
||||
StructuredCloneData::Copy(const StructuredCloneData& aData)
|
||||
{
|
||||
if (!aData.Data()) {
|
||||
if (!aData.mInitialized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -36,8 +36,7 @@ StructuredCloneData::Copy(const StructuredCloneData& aData)
|
||||
mSharedData = aData.SharedData();
|
||||
} else {
|
||||
mSharedData =
|
||||
SharedJSAllocatedData::CreateFromExternalData(aData.Data(),
|
||||
aData.DataLength());
|
||||
SharedJSAllocatedData::CreateFromExternalData(aData.Data());
|
||||
NS_ENSURE_TRUE(mSharedData, false);
|
||||
}
|
||||
|
||||
@ -48,6 +47,8 @@ StructuredCloneData::Copy(const StructuredCloneData& aData)
|
||||
|
||||
MOZ_ASSERT(GetSurfaces().IsEmpty());
|
||||
|
||||
mInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -56,12 +57,12 @@ StructuredCloneData::Read(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult &aRv)
|
||||
{
|
||||
MOZ_ASSERT(Data());
|
||||
MOZ_ASSERT(mInitialized);
|
||||
|
||||
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
ReadFromBuffer(global, aCx, Data(), DataLength(), aValue, aRv);
|
||||
ReadFromBuffer(global, aCx, Data(), aValue, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -78,64 +79,50 @@ StructuredCloneData::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aTransfer,
|
||||
ErrorResult &aRv)
|
||||
{
|
||||
MOZ_ASSERT(!Data());
|
||||
MOZ_ASSERT(!mInitialized);
|
||||
|
||||
StructuredCloneHolder::Write(aCx, aValue, aTransfer, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t* data = nullptr;
|
||||
size_t dataLength = 0;
|
||||
mBuffer->steal(&data, &dataLength);
|
||||
JSStructuredCloneData data;
|
||||
mBuffer->abandon();
|
||||
mBuffer->steal(&data);
|
||||
mBuffer = nullptr;
|
||||
mSharedData = new SharedJSAllocatedData(data, dataLength);
|
||||
mSharedData = new SharedJSAllocatedData(Move(data));
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const
|
||||
{
|
||||
WriteParam(aMsg, DataLength());
|
||||
|
||||
if (DataLength()) {
|
||||
aMsg->WriteBytes(Data(), DataLength());
|
||||
}
|
||||
WriteParam(aMsg, Data());
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
|
||||
PickleIterator* aIter)
|
||||
{
|
||||
MOZ_ASSERT(!Data());
|
||||
|
||||
size_t dataLength = 0;
|
||||
if (!ReadParam(aMsg, aIter, &dataLength)) {
|
||||
MOZ_ASSERT(!mInitialized);
|
||||
JSStructuredCloneData data;
|
||||
if (!ReadParam(aMsg, aIter, &data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dataLength) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mSharedData = SharedJSAllocatedData::AllocateForExternalData(dataLength);
|
||||
NS_ENSURE_TRUE(mSharedData, false);
|
||||
|
||||
if (!aMsg->ReadBytesInto(aIter, mSharedData->Data(), dataLength)) {
|
||||
mSharedData = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
mSharedData = new SharedJSAllocatedData(Move(data));
|
||||
mInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneData::CopyExternalData(const void* aData,
|
||||
StructuredCloneData::CopyExternalData(const char* aData,
|
||||
size_t aDataLength)
|
||||
{
|
||||
MOZ_ASSERT(!Data());
|
||||
MOZ_ASSERT(!mInitialized);
|
||||
mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData,
|
||||
aDataLength);
|
||||
NS_ENSURE_TRUE(mSharedData, false);
|
||||
mInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -24,54 +24,43 @@ namespace ipc {
|
||||
class SharedJSAllocatedData final
|
||||
{
|
||||
public:
|
||||
SharedJSAllocatedData(uint64_t* aData, size_t aDataLength)
|
||||
: mData(aData), mDataLength(aDataLength)
|
||||
{
|
||||
MOZ_ASSERT(mData);
|
||||
}
|
||||
explicit SharedJSAllocatedData(JSStructuredCloneData&& aData)
|
||||
: mData(Move(aData))
|
||||
{ }
|
||||
|
||||
static already_AddRefed<SharedJSAllocatedData>
|
||||
AllocateForExternalData(size_t aDataLength)
|
||||
CreateFromExternalData(const char* aData, size_t aDataLength)
|
||||
{
|
||||
uint64_t* data = Allocate64bitSafely(aDataLength);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSStructuredCloneData buf;
|
||||
buf.WriteBytes(aData, aDataLength);
|
||||
RefPtr<SharedJSAllocatedData> sharedData =
|
||||
new SharedJSAllocatedData(data, aDataLength);
|
||||
new SharedJSAllocatedData(Move(buf));
|
||||
return sharedData.forget();
|
||||
}
|
||||
|
||||
static already_AddRefed<SharedJSAllocatedData>
|
||||
CreateFromExternalData(const void* aData, size_t aDataLength)
|
||||
CreateFromExternalData(const JSStructuredCloneData& aData)
|
||||
{
|
||||
JSStructuredCloneData buf;
|
||||
auto iter = aData.Iter();
|
||||
while (!iter.Done()) {
|
||||
buf.WriteBytes(iter.Data(), iter.RemainingInSegment());
|
||||
iter.Advance(aData, iter.RemainingInSegment());
|
||||
}
|
||||
RefPtr<SharedJSAllocatedData> sharedData =
|
||||
AllocateForExternalData(aDataLength);
|
||||
memcpy(sharedData->Data(), aData, aDataLength);
|
||||
new SharedJSAllocatedData(Move(buf));
|
||||
return sharedData.forget();
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
|
||||
|
||||
uint64_t* Data() const { return mData; }
|
||||
size_t DataLength() const { return mDataLength; }
|
||||
JSStructuredCloneData& Data() { return mData; }
|
||||
size_t DataLength() const { return mData.Size(); }
|
||||
|
||||
private:
|
||||
~SharedJSAllocatedData()
|
||||
{
|
||||
js_free(mData);
|
||||
}
|
||||
~SharedJSAllocatedData() { }
|
||||
|
||||
static uint64_t*
|
||||
Allocate64bitSafely(size_t aSize)
|
||||
{
|
||||
// Structured cloning requires 64-bit aligment.
|
||||
return static_cast<uint64_t*>(js_malloc(std::max(sizeof(uint64_t), aSize)));
|
||||
}
|
||||
|
||||
uint64_t* mData;
|
||||
size_t mDataLength;
|
||||
JSStructuredCloneData mData;
|
||||
};
|
||||
|
||||
class StructuredCloneData : public StructuredCloneHolder
|
||||
@ -81,16 +70,13 @@ public:
|
||||
: StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
|
||||
StructuredCloneHolder::TransferringSupported,
|
||||
StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
|
||||
, mExternalData(nullptr)
|
||||
, mExternalDataLength(0)
|
||||
, mInitialized(false)
|
||||
{}
|
||||
|
||||
StructuredCloneData(const StructuredCloneData&) = delete;
|
||||
|
||||
~StructuredCloneData()
|
||||
{
|
||||
MOZ_ASSERT(!(mExternalData && mSharedData));
|
||||
}
|
||||
{}
|
||||
|
||||
StructuredCloneData&
|
||||
operator=(const StructuredCloneData& aOther) = delete;
|
||||
@ -120,23 +106,31 @@ public:
|
||||
JS::Handle<JS::Value> aTransfers,
|
||||
ErrorResult &aRv);
|
||||
|
||||
void UseExternalData(uint64_t* aData, size_t aDataLength)
|
||||
bool UseExternalData(const JSStructuredCloneData& aData)
|
||||
{
|
||||
MOZ_ASSERT(!Data());
|
||||
mExternalData = aData;
|
||||
mExternalDataLength = aDataLength;
|
||||
auto iter = aData.Iter();
|
||||
bool success = false;
|
||||
mExternalData =
|
||||
aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success);
|
||||
mInitialized = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CopyExternalData(const void* aData, size_t aDataLength);
|
||||
bool CopyExternalData(const char* aData, size_t aDataLength);
|
||||
|
||||
uint64_t* Data() const
|
||||
JSStructuredCloneData& Data()
|
||||
{
|
||||
return mSharedData ? mSharedData->Data() : mExternalData;
|
||||
}
|
||||
|
||||
const JSStructuredCloneData& Data() const
|
||||
{
|
||||
return mSharedData ? mSharedData->Data() : mExternalData;
|
||||
}
|
||||
|
||||
size_t DataLength() const
|
||||
{
|
||||
return mSharedData ? mSharedData->DataLength() : mExternalDataLength;
|
||||
return mSharedData ? mSharedData->DataLength() : mExternalData.Size();
|
||||
}
|
||||
|
||||
SharedJSAllocatedData* SharedData() const
|
||||
@ -149,10 +143,9 @@ public:
|
||||
bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter);
|
||||
|
||||
private:
|
||||
uint64_t* MOZ_NON_OWNING_REF mExternalData;
|
||||
size_t mExternalDataLength;
|
||||
|
||||
JSStructuredCloneData mExternalData;
|
||||
RefPtr<SharedJSAllocatedData> mSharedData;
|
||||
bool mInitialized;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
|
@ -7,14 +7,18 @@ include protocol PBlob;
|
||||
|
||||
include DOMTypes;
|
||||
|
||||
using struct mozilla::SerializedStructuredCloneBuffer
|
||||
from "ipc/IPCMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
||||
struct MessagePortMessage
|
||||
{
|
||||
MessagePortIdentifier[] transferredPorts;
|
||||
uint8_t[] data;
|
||||
SerializedStructuredCloneBuffer data;
|
||||
PBlob[] blobs;
|
||||
MessagePortIdentifier[] transferredPorts;
|
||||
};
|
||||
|
||||
// This protocol is used for the MessageChannel/MessagePort API
|
||||
|
@ -20,66 +20,6 @@ using namespace ipc;
|
||||
|
||||
namespace dom {
|
||||
|
||||
void
|
||||
SharedMessagePortMessage::Read(nsISupports* aParent,
|
||||
JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mData.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* data = reinterpret_cast<uint64_t*>(mData.Elements());
|
||||
size_t dataLen = mData.Length();
|
||||
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
||||
|
||||
ReadFromBuffer(aParent, aCx, data, dataLen, aValue, aRv);
|
||||
NS_WARN_IF(aRv.Failed());
|
||||
|
||||
Free();
|
||||
}
|
||||
|
||||
void
|
||||
SharedMessagePortMessage::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransfer,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
StructuredCloneHolder::Write(aCx, aValue, aTransfer, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
FallibleTArray<uint8_t> cloneData;
|
||||
|
||||
MoveBufferDataToArray(cloneData, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mData.IsEmpty());
|
||||
mData.SwapElements(cloneData);
|
||||
}
|
||||
|
||||
void
|
||||
SharedMessagePortMessage::Free()
|
||||
{
|
||||
if (!mData.IsEmpty()) {
|
||||
auto* data = reinterpret_cast<uint64_t*>(mData.Elements());
|
||||
size_t dataLen = mData.Length();
|
||||
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
||||
|
||||
FreeBuffer(data, dataLen);
|
||||
mData.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
SharedMessagePortMessage::~SharedMessagePortMessage()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedMessagePortMessage::FromSharedToMessagesChild(
|
||||
MessagePortChild* aActor,
|
||||
@ -95,7 +35,8 @@ SharedMessagePortMessage::FromSharedToMessagesChild(
|
||||
|
||||
for (auto& data : aData) {
|
||||
MessagePortMessage* message = aArray.AppendElement();
|
||||
message->data().SwapElements(data->mData);
|
||||
data->mBuffer->abandon();
|
||||
data->mBuffer->steal(&message->data().data);
|
||||
|
||||
const nsTArray<RefPtr<BlobImpl>>& blobImpls = data->BlobImpls();
|
||||
if (!blobImpls.IsEmpty()) {
|
||||
@ -127,7 +68,10 @@ SharedMessagePortMessage::FromMessagesToSharedChild(
|
||||
for (auto& message : aArray) {
|
||||
RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
|
||||
|
||||
data->mData.SwapElements(message.data());
|
||||
data->mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(
|
||||
JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
|
||||
data->mBuffer->adopt(Move(message.data().data), JS_STRUCTURED_CLONE_VERSION,
|
||||
&StructuredCloneHolder::sCallbacks, data.get());
|
||||
|
||||
const nsTArray<PBlobChild*>& blobs = message.blobsChild();
|
||||
if (!blobs.IsEmpty()) {
|
||||
@ -167,7 +111,8 @@ SharedMessagePortMessage::FromSharedToMessagesParent(
|
||||
|
||||
for (auto& data : aData) {
|
||||
MessagePortMessage* message = aArray.AppendElement(mozilla::fallible);
|
||||
message->data().SwapElements(data->mData);
|
||||
data->mBuffer->abandon();
|
||||
data->mBuffer->steal(&message->data().data);
|
||||
|
||||
const nsTArray<RefPtr<BlobImpl>>& blobImpls = data->BlobImpls();
|
||||
if (!blobImpls.IsEmpty()) {
|
||||
@ -201,7 +146,10 @@ SharedMessagePortMessage::FromMessagesToSharedParent(
|
||||
for (auto& message : aArray) {
|
||||
RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
|
||||
|
||||
data->mData.SwapElements(message.data());
|
||||
data->mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(
|
||||
JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
|
||||
data->mBuffer->adopt(Move(message.data().data), JS_STRUCTURED_CLONE_VERSION,
|
||||
&StructuredCloneHolder::sCallbacks, data.get());
|
||||
|
||||
const nsTArray<PBlobParent*>& blobs = message.blobsParent();
|
||||
if (!blobs.IsEmpty()) {
|
||||
|
@ -20,25 +20,11 @@ class SharedMessagePortMessage final : public StructuredCloneHolder
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(SharedMessagePortMessage)
|
||||
|
||||
nsTArray<uint8_t> mData;
|
||||
|
||||
SharedMessagePortMessage()
|
||||
: StructuredCloneHolder(CloningSupported, TransferringSupported,
|
||||
StructuredCloneScope::DifferentProcess)
|
||||
{}
|
||||
|
||||
void Read(nsISupports* aParent,
|
||||
JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransfer,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void Free();
|
||||
|
||||
static void
|
||||
FromSharedToMessagesChild(
|
||||
MessagePortChild* aActor,
|
||||
@ -62,7 +48,7 @@ public:
|
||||
FallibleTArray<RefPtr<SharedMessagePortMessage>>& aData);
|
||||
|
||||
private:
|
||||
~SharedMessagePortMessage();
|
||||
~SharedMessagePortMessage() {}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -421,12 +421,23 @@ bool Pickle::FlattenBytes(PickleIterator* iter, const char** data, uint32_t leng
|
||||
return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
|
||||
}
|
||||
|
||||
bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers) const
|
||||
bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers,
|
||||
uint32_t alignment) const
|
||||
{
|
||||
DCHECK(iter);
|
||||
DCHECK(buffers);
|
||||
DCHECK(alignment == 4 || alignment == 8);
|
||||
DCHECK(intptr_t(header_) % alignment == 0);
|
||||
|
||||
if (AlignInt(length) < length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t padding_len = intptr_t(iter->iter_.Data()) % alignment;
|
||||
if (!iter->iter_.AdvanceAcrossSegments(buffers_, padding_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success;
|
||||
*buffers = const_cast<BufferList*>(&buffers_)->Extract(iter->iter_, length, &success);
|
||||
if (!success) {
|
||||
|
@ -113,7 +113,8 @@ class Pickle {
|
||||
MOZ_MUST_USE bool ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const;
|
||||
MOZ_MUST_USE bool FlattenBytes(PickleIterator* iter, const char** data, uint32_t length,
|
||||
uint32_t alignment = sizeof(memberAlignmentType));
|
||||
MOZ_MUST_USE bool ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers) const;
|
||||
MOZ_MUST_USE bool ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers,
|
||||
uint32_t alignment = sizeof(memberAlignmentType)) const;
|
||||
|
||||
// Safer version of ReadInt() checks for the result not being negative.
|
||||
// Use it for reading the object sizes.
|
||||
|
@ -64,21 +64,31 @@ struct null_t {
|
||||
bool operator==(const null_t&) const { return true; }
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS SerializedStructuredCloneBuffer
|
||||
struct SerializedStructuredCloneBuffer final
|
||||
{
|
||||
SerializedStructuredCloneBuffer()
|
||||
: data(nullptr), dataLength(0)
|
||||
{ }
|
||||
SerializedStructuredCloneBuffer&
|
||||
operator=(const SerializedStructuredCloneBuffer& aOther)
|
||||
{
|
||||
data.Clear();
|
||||
auto iter = aOther.data.Iter();
|
||||
while (!iter.Done()) {
|
||||
data.WriteBytes(iter.Data(), iter.RemainingInSegment());
|
||||
iter.Advance(aOther.data, iter.RemainingInSegment());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const SerializedStructuredCloneBuffer& aOther) const
|
||||
{
|
||||
return this->data == aOther.data &&
|
||||
this->dataLength == aOther.dataLength;
|
||||
// The copy assignment operator and the equality operator are
|
||||
// needed by the IPDL generated code. We relied on the copy
|
||||
// assignment operator at some places but we never use the
|
||||
// equality operator.
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t* data;
|
||||
size_t dataLength;
|
||||
JSStructuredCloneData data;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
@ -703,6 +713,55 @@ struct ParamTraits<mozilla::net::WebSocketFrameData>
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<JSStructuredCloneData>
|
||||
{
|
||||
typedef JSStructuredCloneData paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
MOZ_ASSERT(!(aParam.Size() % sizeof(uint64_t)));
|
||||
WriteParam(aMsg, aParam.Size());
|
||||
auto iter = aParam.Iter();
|
||||
while (!iter.Done()) {
|
||||
aMsg->WriteBytes(iter.Data(), iter.RemainingInSegment(), sizeof(uint64_t));
|
||||
iter.Advance(aParam, iter.RemainingInSegment());
|
||||
}
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
size_t length = 0;
|
||||
if (!ReadParam(aMsg, aIter, &length)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(!(length % sizeof(uint64_t)));
|
||||
|
||||
mozilla::BufferList<InfallibleAllocPolicy> buffers(0, 0, 4096);
|
||||
|
||||
// Borrowing is not suitable to use for IPC to hand out data
|
||||
// because we often want to store the data somewhere for
|
||||
// processing after IPC has released the underlying buffers. One
|
||||
// case is PContentChild::SendGetXPCOMProcessAttributes. We can't
|
||||
// return a borrowed buffer because the out param outlives the
|
||||
// IPDL callback.
|
||||
if (length && !aMsg->ExtractBuffers(aIter, length, &buffers, sizeof(uint64_t))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success;
|
||||
mozilla::BufferList<js::SystemAllocPolicy> out =
|
||||
buffers.MoveFallible<js::SystemAllocPolicy>(&success);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*aResult = JSStructuredCloneData(Move(out));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
|
||||
{
|
||||
@ -710,38 +769,17 @@ struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.dataLength);
|
||||
if (aParam.dataLength) {
|
||||
// Structured clone data must be 64-bit aligned.
|
||||
aMsg->WriteBytes(aParam.data, aParam.dataLength, sizeof(uint64_t));
|
||||
}
|
||||
WriteParam(aMsg, aParam.data);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &aResult->dataLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aResult->dataLength) {
|
||||
aResult->data = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char** buffer =
|
||||
const_cast<const char**>(reinterpret_cast<char**>(&aResult->data));
|
||||
// Structured clone data must be 64-bit aligned.
|
||||
if (!const_cast<Message*>(aMsg)->FlattenBytes(aIter, buffer, aResult->dataLength,
|
||||
sizeof(uint64_t))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return ReadParam(aMsg, aIter, &aResult->data);
|
||||
}
|
||||
|
||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||
{
|
||||
LogParam(aParam.dataLength, aLog);
|
||||
LogParam(aParam.data.Size(), aLog);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
#ifndef js_StructuredClone_h
|
||||
#define js_StructuredClone_h
|
||||
|
||||
#include "mozilla/BufferList.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jstypes.h"
|
||||
@ -130,9 +132,9 @@ typedef bool (*TransferStructuredCloneOp)(JSContext* cx,
|
||||
uint64_t* extraData);
|
||||
|
||||
/**
|
||||
* Called when JS_ClearStructuredClone has to free an unknown transferable
|
||||
* object. Note that it should never trigger a garbage collection (and will
|
||||
* assert in a debug build if it does.)
|
||||
* Called when freeing an unknown transferable object. Note that it
|
||||
* should never trigger a garbage collection (and will assert in a
|
||||
* debug build if it does.)
|
||||
*/
|
||||
typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership,
|
||||
void* content, uint64_t extraData, void* closure);
|
||||
@ -152,30 +154,71 @@ struct JSStructuredCloneCallbacks {
|
||||
FreeTransferStructuredCloneOp freeTransfer;
|
||||
};
|
||||
|
||||
enum OwnTransferablePolicy {
|
||||
OwnsTransferablesIfAny,
|
||||
IgnoreTransferablesIfAny,
|
||||
NoTransferables
|
||||
};
|
||||
|
||||
class JSStructuredCloneData : public mozilla::BufferList<js::SystemAllocPolicy>
|
||||
{
|
||||
typedef js::SystemAllocPolicy AllocPolicy;
|
||||
typedef mozilla::BufferList<js::SystemAllocPolicy> BufferList;
|
||||
|
||||
static const size_t kInitialSize = 0;
|
||||
static const size_t kInitialCapacity = 4096;
|
||||
static const size_t kStandardCapacity = 4096;
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks_;
|
||||
void* closure_;
|
||||
OwnTransferablePolicy ownTransferables_;
|
||||
|
||||
void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks,
|
||||
void* closure,
|
||||
OwnTransferablePolicy policy) {
|
||||
callbacks_ = callbacks;
|
||||
closure_ = closure;
|
||||
ownTransferables_ = policy;
|
||||
}
|
||||
|
||||
friend struct JSStructuredCloneWriter;
|
||||
friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer);
|
||||
|
||||
public:
|
||||
explicit JSStructuredCloneData(AllocPolicy aAP = AllocPolicy())
|
||||
: BufferList(kInitialSize, kInitialCapacity, kStandardCapacity, aAP)
|
||||
, callbacks_(nullptr)
|
||||
, closure_(nullptr)
|
||||
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
|
||||
{}
|
||||
MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers)
|
||||
: BufferList(Move(buffers))
|
||||
, callbacks_(nullptr)
|
||||
, closure_(nullptr)
|
||||
, ownTransferables_(OwnTransferablePolicy::NoTransferables)
|
||||
{}
|
||||
JSStructuredCloneData(JSStructuredCloneData&& other) = default;
|
||||
JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default;
|
||||
~JSStructuredCloneData();
|
||||
|
||||
using BufferList::BufferList;
|
||||
};
|
||||
|
||||
/** Note: if the *data contains transferable objects, it can be read only once. */
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes, uint32_t version,
|
||||
JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, uint32_t version,
|
||||
JS::StructuredCloneScope scope,
|
||||
JS::MutableHandleValue vp,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
|
||||
|
||||
/**
|
||||
* Note: On success, the caller is responsible for calling
|
||||
* JS_ClearStructuredClone(*datap, nbytes, optionalCallbacks, closure).
|
||||
*/
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, uint64_t** datap, size_t* nbytesp,
|
||||
JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, JSStructuredCloneData* data,
|
||||
JS::StructuredCloneScope scope,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void* closure, JS::HandleValue transferable);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void *closure, bool freeData = true);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes, bool* hasTransferable);
|
||||
JS_StructuredCloneHasTransferables(JSStructuredCloneData& data, bool* hasTransferable);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
|
||||
@ -184,38 +227,29 @@ JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
|
||||
/** RAII sugar for JS_WriteStructuredClone. */
|
||||
class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
|
||||
const JS::StructuredCloneScope scope_;
|
||||
uint64_t* data_;
|
||||
size_t nbytes_;
|
||||
JSStructuredCloneData data_;
|
||||
uint32_t version_;
|
||||
enum {
|
||||
OwnsTransferablesIfAny,
|
||||
IgnoreTransferablesIfAny,
|
||||
NoTransferables
|
||||
} ownTransferables_;
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks_;
|
||||
void* closure_;
|
||||
|
||||
public:
|
||||
JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
|
||||
const JSStructuredCloneCallbacks* callbacks, void* closure)
|
||||
: scope_(scope), data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
|
||||
ownTransferables_(NoTransferables),
|
||||
callbacks_(callbacks), closure_(closure)
|
||||
{}
|
||||
: scope_(scope), version_(JS_STRUCTURED_CLONE_VERSION)
|
||||
{
|
||||
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
|
||||
}
|
||||
|
||||
JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
|
||||
JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other);
|
||||
|
||||
~JSAutoStructuredCloneBuffer() { clear(); }
|
||||
|
||||
uint64_t* data() const { return data_; }
|
||||
size_t nbytes() const { return nbytes_; }
|
||||
JSStructuredCloneData& data() { return data_; }
|
||||
bool empty() const { return !data_.Size(); }
|
||||
|
||||
void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
/** Copy some memory. It will be automatically freed by the destructor. */
|
||||
bool copy(const uint64_t* data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
|
||||
bool copy(const JSStructuredCloneData& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
|
||||
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
/**
|
||||
@ -223,24 +257,22 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
|
||||
* data must have been allocated by the JS engine (e.g., extracted via
|
||||
* JSAutoStructuredCloneBuffer::steal).
|
||||
*/
|
||||
void adopt(uint64_t* data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
|
||||
void adopt(JSStructuredCloneData&& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
|
||||
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
|
||||
|
||||
/**
|
||||
* Release the buffer and transfer ownership to the caller. The caller is
|
||||
* responsible for calling JS_ClearStructuredClone or feeding the memory
|
||||
* back to JSAutoStructuredCloneBuffer::adopt.
|
||||
* Release the buffer and transfer ownership to the caller.
|
||||
*/
|
||||
void steal(uint64_t** datap, size_t* nbytesp, uint32_t* versionp=nullptr,
|
||||
void steal(JSStructuredCloneData* data, uint32_t* versionp=nullptr,
|
||||
const JSStructuredCloneCallbacks** callbacks=nullptr, void** closure=nullptr);
|
||||
|
||||
/**
|
||||
* Abandon ownership of any transferable objects stored in the buffer,
|
||||
* without freeing the buffer itself. Useful when copying the data out into
|
||||
* an external container, though note that you will need to use adopt() or
|
||||
* JS_ClearStructuredClone to properly release that data eventually.
|
||||
* an external container, though note that you will need to use adopt() to
|
||||
* properly release that data eventually.
|
||||
*/
|
||||
void abandon() { ownTransferables_ = IgnoreTransferablesIfAny; }
|
||||
void abandon() { data_.ownTransferables_ = OwnTransferablePolicy::IgnoreTransferablesIfAny; }
|
||||
|
||||
bool read(JSContext* cx, JS::MutableHandleValue vp,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
|
||||
|
@ -2075,36 +2075,31 @@ class CloneBufferObject : public NativeObject {
|
||||
Rooted<CloneBufferObject*> obj(cx, Create(cx));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
uint64_t* datap;
|
||||
size_t nbytes;
|
||||
buffer->steal(&datap, &nbytes);
|
||||
obj->setData(datap);
|
||||
obj->setNBytes(nbytes);
|
||||
auto data = js::MakeUnique<JSStructuredCloneData>();
|
||||
if (!data) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
buffer->steal(data.get());
|
||||
obj->setData(data.release());
|
||||
return obj;
|
||||
}
|
||||
|
||||
uint64_t* data() const {
|
||||
return static_cast<uint64_t*>(getReservedSlot(DATA_SLOT).toPrivate());
|
||||
JSStructuredCloneData* data() const {
|
||||
return static_cast<JSStructuredCloneData*>(getReservedSlot(DATA_SLOT).toPrivate());
|
||||
}
|
||||
|
||||
void setData(uint64_t* aData) {
|
||||
void setData(JSStructuredCloneData* aData) {
|
||||
MOZ_ASSERT(!data());
|
||||
setReservedSlot(DATA_SLOT, PrivateValue(aData));
|
||||
}
|
||||
|
||||
size_t nbytes() const {
|
||||
return getReservedSlot(LENGTH_SLOT).toInt32();
|
||||
}
|
||||
|
||||
void setNBytes(size_t nbytes) {
|
||||
MOZ_ASSERT(nbytes <= UINT32_MAX);
|
||||
setReservedSlot(LENGTH_SLOT, Int32Value(nbytes));
|
||||
}
|
||||
|
||||
// Discard an owned clone buffer.
|
||||
void discard() {
|
||||
if (data())
|
||||
JS_ClearStructuredClone(data(), nbytes(), nullptr, nullptr);
|
||||
if (data()) {
|
||||
JSAutoStructuredCloneBuffer clonebuf(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
|
||||
clonebuf.adopt(Move(*data()));
|
||||
}
|
||||
setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
|
||||
}
|
||||
|
||||
@ -2131,8 +2126,12 @@ class CloneBufferObject : public NativeObject {
|
||||
char* str = JS_EncodeString(cx, args[0].toString());
|
||||
if (!str)
|
||||
return false;
|
||||
obj->setData(reinterpret_cast<uint64_t*>(str));
|
||||
obj->setNBytes(JS_GetStringLength(args[0].toString()));
|
||||
size_t nbytes = JS_GetStringLength(args[0].toString());
|
||||
MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
|
||||
auto buf = js::MakeUnique<JSStructuredCloneData>(nbytes, nbytes, nbytes);
|
||||
js_memcpy(buf->Start(), str, nbytes);
|
||||
JS_free(cx, str);
|
||||
obj->setData(buf.release());
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
@ -2160,7 +2159,7 @@ class CloneBufferObject : public NativeObject {
|
||||
}
|
||||
|
||||
bool hasTransferable;
|
||||
if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
|
||||
if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
|
||||
return false;
|
||||
|
||||
if (hasTransferable) {
|
||||
@ -2168,7 +2167,11 @@ class CloneBufferObject : public NativeObject {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString* str = JS_NewStringCopyN(cx, reinterpret_cast<char*>(obj->data()), obj->nbytes());
|
||||
size_t size = obj->data()->Size();
|
||||
UniqueChars buffer(static_cast<char*>(js_malloc(size)));
|
||||
auto iter = obj->data()->Iter();
|
||||
obj->data()->ReadBytes(iter, buffer.get(), size);
|
||||
JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
|
||||
if (!str)
|
||||
return false;
|
||||
args.rval().setString(str);
|
||||
@ -2249,15 +2252,14 @@ Deserialize(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
|
||||
bool hasTransferable;
|
||||
if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
|
||||
if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
|
||||
return false;
|
||||
|
||||
RootedValue deserialized(cx);
|
||||
if (!JS_ReadStructuredClone(cx, obj->data(), obj->nbytes(),
|
||||
if (!JS_ReadStructuredClone(cx, *obj->data(),
|
||||
JS_STRUCTURED_CLONE_VERSION,
|
||||
JS::StructuredCloneScope::SameProcessSameThread,
|
||||
&deserialized, nullptr, nullptr))
|
||||
{
|
||||
&deserialized, nullptr, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
args.rval().set(deserialized);
|
||||
|
@ -152,6 +152,60 @@ PairToUInt64(uint32_t tag, uint32_t data)
|
||||
|
||||
namespace js {
|
||||
|
||||
template<typename T, typename AllocPolicy>
|
||||
struct BufferIterator {
|
||||
typedef mozilla::BufferList<AllocPolicy> BufferList;
|
||||
|
||||
explicit BufferIterator(BufferList& buffer)
|
||||
: mBuffer(buffer)
|
||||
, mIter(buffer.Iter())
|
||||
{
|
||||
JS_STATIC_ASSERT(8 % sizeof(T) == 0);
|
||||
}
|
||||
|
||||
BufferIterator operator++(int) {
|
||||
BufferIterator ret = *this;
|
||||
if (!mIter.AdvanceAcrossSegments(mBuffer, sizeof(T))) {
|
||||
MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
BufferIterator& operator+=(size_t size) {
|
||||
if (!mIter.AdvanceAcrossSegments(mBuffer, size)) {
|
||||
MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void next() {
|
||||
if (!mIter.AdvanceAcrossSegments(mBuffer, sizeof(T))) {
|
||||
MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
|
||||
}
|
||||
}
|
||||
|
||||
bool done() const {
|
||||
return mIter.Done();
|
||||
}
|
||||
|
||||
bool readBytes(char* outData, size_t size) {
|
||||
return mBuffer.ReadBytes(mIter, outData, size);
|
||||
}
|
||||
|
||||
void write(const T& data) {
|
||||
MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
|
||||
*reinterpret_cast<T*>(mIter.Data()) = data;
|
||||
}
|
||||
|
||||
T peek() const {
|
||||
MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
|
||||
return *reinterpret_cast<T*>(mIter.Data());
|
||||
}
|
||||
|
||||
BufferList& mBuffer;
|
||||
typename BufferList::IterImpl mIter;
|
||||
};
|
||||
|
||||
struct SCOutput {
|
||||
public:
|
||||
explicit SCOutput(JSContext* cx);
|
||||
@ -169,24 +223,29 @@ struct SCOutput {
|
||||
template <class T>
|
||||
bool writeArray(const T* p, size_t nbytes);
|
||||
|
||||
bool extractBuffer(uint64_t** datap, size_t* sizep);
|
||||
bool extractBuffer(JSStructuredCloneData* data);
|
||||
void discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure);
|
||||
|
||||
uint64_t count() const { return buf.length(); }
|
||||
uint64_t* rawBuffer() { return buf.begin(); }
|
||||
uint64_t count() const { return buf.Size() / sizeof(uint64_t); }
|
||||
BufferIterator<uint64_t, TempAllocPolicy> iter() {
|
||||
return BufferIterator<uint64_t, TempAllocPolicy>(buf);
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* cx;
|
||||
Vector<uint64_t> buf;
|
||||
mozilla::BufferList<TempAllocPolicy> buf;
|
||||
};
|
||||
|
||||
class SCInput {
|
||||
typedef js::BufferIterator<uint64_t, SystemAllocPolicy> BufferIterator;
|
||||
|
||||
public:
|
||||
SCInput(JSContext* cx, uint64_t* data, size_t nbytes);
|
||||
SCInput(JSContext* cx, JSStructuredCloneData& data);
|
||||
|
||||
JSContext* context() const { return cx; }
|
||||
|
||||
static void getPtr(const uint64_t* buffer, void** ptr);
|
||||
static void getPair(const uint64_t* buffer, uint32_t* tagp, uint32_t* datap);
|
||||
static void getPtr(uint64_t data, void** ptr);
|
||||
static void getPair(uint64_t data, uint32_t* tagp, uint32_t* datap);
|
||||
|
||||
bool read(uint64_t* p);
|
||||
bool readNativeEndian(uint64_t* p);
|
||||
@ -200,8 +259,7 @@ class SCInput {
|
||||
bool get(uint64_t* p);
|
||||
bool getPair(uint32_t* tagp, uint32_t* datap);
|
||||
|
||||
uint64_t* tell() const { return point; }
|
||||
uint64_t* end() const { return bufEnd; }
|
||||
BufferIterator tell() const { return point; }
|
||||
|
||||
template <class T>
|
||||
bool readArray(T* p, size_t nelems);
|
||||
@ -219,8 +277,7 @@ class SCInput {
|
||||
}
|
||||
|
||||
JSContext* cx;
|
||||
uint64_t* point;
|
||||
uint64_t* bufEnd;
|
||||
BufferIterator point;
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
@ -302,8 +359,13 @@ struct JSStructuredCloneWriter {
|
||||
|
||||
SCOutput& output() { return out; }
|
||||
|
||||
bool extractBuffer(uint64_t** datap, size_t* sizep) {
|
||||
return out.extractBuffer(datap, sizep);
|
||||
bool extractBuffer(JSStructuredCloneData* data) {
|
||||
bool success = out.extractBuffer(data);
|
||||
if (success) {
|
||||
data->setOptionalCallbacks(callbacks, closure,
|
||||
OwnTransferablePolicy::OwnsTransferablesIfAny);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
JS::StructuredCloneScope cloneScope() const { return scope; }
|
||||
@ -422,21 +484,21 @@ ReportDataCloneError(JSContext* cx,
|
||||
}
|
||||
|
||||
bool
|
||||
WriteStructuredClone(JSContext* cx, HandleValue v, uint64_t** bufp, size_t* nbytesp,
|
||||
WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp,
|
||||
JS::StructuredCloneScope scope,
|
||||
const JSStructuredCloneCallbacks* cb, void* cbClosure,
|
||||
Value transferable)
|
||||
{
|
||||
JSStructuredCloneWriter w(cx, scope, cb, cbClosure, transferable);
|
||||
return w.init() && w.write(v) && w.extractBuffer(bufp, nbytesp);
|
||||
return w.init() && w.write(v) && w.extractBuffer(bufp);
|
||||
}
|
||||
|
||||
bool
|
||||
ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes,
|
||||
ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data,
|
||||
JS::StructuredCloneScope scope, MutableHandleValue vp,
|
||||
const JSStructuredCloneCallbacks* cb, void* cbClosure)
|
||||
{
|
||||
SCInput in(cx, data, nbytes);
|
||||
SCInput in(cx, data);
|
||||
JSStructuredCloneReader r(in, scope, cb, cbClosure);
|
||||
return r.read(vp);
|
||||
}
|
||||
@ -444,24 +506,25 @@ ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes,
|
||||
// If the given buffer contains Transferables, free them. Note that custom
|
||||
// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
|
||||
// delete their transferables.
|
||||
template<typename AllocPolicy>
|
||||
static void
|
||||
DiscardTransferables(uint64_t* buffer, size_t nbytes,
|
||||
DiscardTransferables(mozilla::BufferList<AllocPolicy>& buffer,
|
||||
const JSStructuredCloneCallbacks* cb, void* cbClosure)
|
||||
{
|
||||
MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
|
||||
uint64_t* end = buffer + nbytes / sizeof(uint64_t);
|
||||
uint64_t* point = buffer;
|
||||
if (point == end)
|
||||
auto point = BufferIterator<uint64_t, AllocPolicy>(buffer);
|
||||
if (point.done())
|
||||
return; // Empty buffer
|
||||
|
||||
uint32_t tag, data;
|
||||
SCInput::getPair(point++, &tag, &data);
|
||||
SCInput::getPair(point.peek(), &tag, &data);
|
||||
point.next();
|
||||
|
||||
if (tag == SCTAG_HEADER) {
|
||||
if (point == end)
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
SCInput::getPair(point++, &tag, &data);
|
||||
SCInput::getPair(point.peek(), &tag, &data);
|
||||
point.next();
|
||||
}
|
||||
|
||||
if (tag != SCTAG_TRANSFER_MAP_HEADER)
|
||||
@ -473,26 +536,30 @@ DiscardTransferables(uint64_t* buffer, size_t nbytes,
|
||||
// freeTransfer should not GC
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
if (point == end)
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
uint64_t numTransferables = LittleEndian::readUint64(point++);
|
||||
uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek());
|
||||
point.next();
|
||||
while (numTransferables--) {
|
||||
if (point == end)
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
uint32_t ownership;
|
||||
SCInput::getPair(point++, &tag, &ownership);
|
||||
SCInput::getPair(point.peek(), &tag, &ownership);
|
||||
point.next();
|
||||
MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
|
||||
if (point == end)
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
void* content;
|
||||
SCInput::getPtr(point++, &content);
|
||||
if (point == end)
|
||||
SCInput::getPtr(point.peek(), &content);
|
||||
point.next();
|
||||
if (point.done())
|
||||
return;
|
||||
|
||||
uint64_t extraData = LittleEndian::readUint64(point++);
|
||||
uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek());
|
||||
point.next();
|
||||
|
||||
if (ownership < JS::SCTAG_TMO_FIRST_OWNED)
|
||||
continue;
|
||||
@ -514,46 +581,51 @@ DiscardTransferables(uint64_t* buffer, size_t nbytes,
|
||||
}
|
||||
|
||||
static bool
|
||||
StructuredCloneHasTransferObjects(const uint64_t* data, size_t nbytes)
|
||||
StructuredCloneHasTransferObjects(const JSStructuredCloneData& data)
|
||||
{
|
||||
if (!data)
|
||||
auto iter = data.Iter();
|
||||
|
||||
if (data.Size() < sizeof(uint64_t))
|
||||
return false;
|
||||
|
||||
uint64_t u = LittleEndian::readUint64(data);
|
||||
uint64_t u;
|
||||
data.ReadBytes(iter, reinterpret_cast<char*>(&u), sizeof(u));
|
||||
uint32_t tag = uint32_t(u >> 32);
|
||||
return (tag == SCTAG_TRANSFER_MAP_HEADER);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
SCInput::SCInput(JSContext* cx, uint64_t* data, size_t nbytes)
|
||||
: cx(cx), point(data), bufEnd(data + nbytes / 8)
|
||||
SCInput::SCInput(JSContext* cx, JSStructuredCloneData& data)
|
||||
: cx(cx), point(data)
|
||||
{
|
||||
// On 32-bit, we sometimes construct an SCInput from an SCOutput buffer,
|
||||
// which is not guaranteed to be 8-byte aligned
|
||||
MOZ_ASSERT((uintptr_t(data) & (sizeof(int) - 1)) == 0);
|
||||
MOZ_ASSERT((nbytes & 7) == 0);
|
||||
|
||||
static_assert(JSStructuredCloneData::kSegmentAlignment % 8 == 0,
|
||||
"structured clone buffer reads should be aligned");
|
||||
MOZ_ASSERT(data.Size() % 8 == 0);
|
||||
}
|
||||
|
||||
bool
|
||||
SCInput::read(uint64_t* p)
|
||||
{
|
||||
if (point == bufEnd) {
|
||||
if (point.done()) {
|
||||
*p = 0; /* initialize to shut GCC up */
|
||||
return reportTruncated();
|
||||
}
|
||||
*p = LittleEndian::readUint64(point++);
|
||||
*p = NativeEndian::swapFromLittleEndian(point.peek());
|
||||
point.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SCInput::readNativeEndian(uint64_t* p)
|
||||
{
|
||||
if (point == bufEnd) {
|
||||
if (point.done()) {
|
||||
*p = 0; /* initialize to shut GCC up */
|
||||
return reportTruncated();
|
||||
}
|
||||
*p = *(point++);
|
||||
*p = point.peek();
|
||||
point.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -572,9 +644,9 @@ SCInput::readPair(uint32_t* tagp, uint32_t* datap)
|
||||
bool
|
||||
SCInput::get(uint64_t* p)
|
||||
{
|
||||
if (point == bufEnd)
|
||||
if (point.done())
|
||||
return reportTruncated();
|
||||
*p = LittleEndian::readUint64(point);
|
||||
*p = NativeEndian::swapFromLittleEndian(point.peek());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -591,9 +663,9 @@ SCInput::getPair(uint32_t* tagp, uint32_t* datap)
|
||||
}
|
||||
|
||||
void
|
||||
SCInput::getPair(const uint64_t* p, uint32_t* tagp, uint32_t* datap)
|
||||
SCInput::getPair(uint64_t data, uint32_t* tagp, uint32_t* datap)
|
||||
{
|
||||
uint64_t u = LittleEndian::readUint64(p);
|
||||
uint64_t u = NativeEndian::swapFromLittleEndian(data);
|
||||
*tagp = uint32_t(u >> 32);
|
||||
*datap = uint32_t(u);
|
||||
}
|
||||
@ -613,23 +685,24 @@ SCInput::readDouble(double* p)
|
||||
|
||||
template <typename T>
|
||||
static void
|
||||
copyAndSwapFromLittleEndian(T* dest, const void* src, size_t nelems)
|
||||
swapFromLittleEndianInPlace(T* ptr, size_t nelems)
|
||||
{
|
||||
if (nelems > 0)
|
||||
NativeEndian::copyAndSwapFromLittleEndian(dest, src, nelems);
|
||||
NativeEndian::swapFromLittleEndianInPlace(ptr, nelems);
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
copyAndSwapFromLittleEndian(uint8_t* dest, const void* src, size_t nelems)
|
||||
{
|
||||
memcpy(dest, src, nelems);
|
||||
}
|
||||
swapFromLittleEndianInPlace(uint8_t* ptr, size_t nelems)
|
||||
{}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
SCInput::readArray(T* p, size_t nelems)
|
||||
{
|
||||
if (!nelems)
|
||||
return true;
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
|
||||
|
||||
/*
|
||||
@ -637,11 +710,17 @@ SCInput::readArray(T* p, size_t nelems)
|
||||
* larger than the remaining data.
|
||||
*/
|
||||
size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
|
||||
if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(bufEnd - point))
|
||||
if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems)
|
||||
return reportTruncated();
|
||||
|
||||
copyAndSwapFromLittleEndian(p, point, nelems);
|
||||
point += nwords;
|
||||
size_t size = sizeof(T) * nelems;
|
||||
if (!point.readBytes(reinterpret_cast<char*>(p), size))
|
||||
return false;
|
||||
|
||||
swapFromLittleEndianInPlace(p, nelems);
|
||||
|
||||
point += sizeof(uint64_t) * nwords - size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -666,11 +745,11 @@ SCInput::readChars(char16_t* p, size_t nchars)
|
||||
}
|
||||
|
||||
void
|
||||
SCInput::getPtr(const uint64_t* p, void** ptr)
|
||||
SCInput::getPtr(uint64_t data, void** ptr)
|
||||
{
|
||||
// No endianness conversion is used for pointers, since they are not sent
|
||||
// across address spaces anyway.
|
||||
*ptr = reinterpret_cast<void*>(*p);
|
||||
*ptr = reinterpret_cast<void*>(data);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -683,12 +762,17 @@ SCInput::readPtr(void** p)
|
||||
return true;
|
||||
}
|
||||
|
||||
SCOutput::SCOutput(JSContext* cx) : cx(cx), buf(cx) {}
|
||||
SCOutput::SCOutput(JSContext* cx)
|
||||
: cx(cx)
|
||||
, buf(0, 0, 4096, cx)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
SCOutput::write(uint64_t u)
|
||||
{
|
||||
return buf.append(NativeEndian::swapToLittleEndian(u));
|
||||
uint64_t v = NativeEndian::swapToLittleEndian(u);
|
||||
return buf.WriteBytes(reinterpret_cast<char*>(&v), sizeof(u));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -719,26 +803,25 @@ SCOutput::writeDouble(double d)
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void
|
||||
copyAndSwapToLittleEndian(void* dest, const T* src, size_t nelems)
|
||||
static T
|
||||
swapToLittleEndian(T value)
|
||||
{
|
||||
if (nelems > 0)
|
||||
NativeEndian::copyAndSwapToLittleEndian(dest, src, nelems);
|
||||
return NativeEndian::swapToLittleEndian(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
copyAndSwapToLittleEndian(void* dest, const uint8_t* src, size_t nelems)
|
||||
uint8_t
|
||||
swapToLittleEndian(uint8_t value)
|
||||
{
|
||||
memcpy(dest, src, nelems);
|
||||
return value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
SCOutput::writeArray(const T* p, size_t nelems)
|
||||
{
|
||||
MOZ_ASSERT(8 % sizeof(T) == 0);
|
||||
MOZ_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
|
||||
JS_STATIC_ASSERT(8 % sizeof(T) == 0);
|
||||
JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
|
||||
|
||||
if (nelems == 0)
|
||||
return true;
|
||||
@ -747,15 +830,22 @@ SCOutput::writeArray(const T* p, size_t nelems)
|
||||
ReportAllocationOverflow(context());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nelems; i++) {
|
||||
T value = swapToLittleEndian(p[i]);
|
||||
if (!buf.WriteBytes(reinterpret_cast<char*>(&value), sizeof(value)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// zero-pad to 8 bytes boundary
|
||||
size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
|
||||
size_t start = buf.length();
|
||||
if (!buf.growByUninitialized(nwords))
|
||||
return false;
|
||||
size_t padbytes = sizeof(uint64_t) * nwords - sizeof(T) * nelems;
|
||||
char zero = 0;
|
||||
for (size_t i = 0; i < padbytes; i++) {
|
||||
if (!buf.WriteBytes(&zero, sizeof(zero)))
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.back() = 0; /* zero-pad to an 8-byte boundary */
|
||||
|
||||
T* q = (T*) &buf[start];
|
||||
copyAndSwapToLittleEndian(q, p, nelems);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -788,27 +878,42 @@ SCOutput::writePtr(const void* p)
|
||||
}
|
||||
|
||||
bool
|
||||
SCOutput::extractBuffer(uint64_t** datap, size_t* sizep)
|
||||
SCOutput::extractBuffer(JSStructuredCloneData* data)
|
||||
{
|
||||
*sizep = buf.length() * sizeof(uint64_t);
|
||||
return (*datap = buf.extractOrCopyRawBuffer()) != nullptr;
|
||||
bool success;
|
||||
mozilla::BufferList<SystemAllocPolicy> out =
|
||||
buf.MoveFallible<SystemAllocPolicy>(&success);
|
||||
if (!success) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
*data = JSStructuredCloneData(Move(out));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SCOutput::discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure)
|
||||
{
|
||||
DiscardTransferables(buf, cb, cbClosure);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
JSStructuredCloneData::~JSStructuredCloneData()
|
||||
{
|
||||
if (!Size())
|
||||
return;
|
||||
if (ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
|
||||
DiscardTransferables(*this, callbacks_, closure_);
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
|
||||
|
||||
JSStructuredCloneWriter::~JSStructuredCloneWriter()
|
||||
{
|
||||
// Free any transferable data left lying around in the buffer
|
||||
uint64_t* data;
|
||||
size_t size;
|
||||
{
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!extractBuffer(&data, &size))
|
||||
oomUnsafe.crash("Unable to extract clone buffer");
|
||||
DiscardTransferables(data, size, callbacks, closure);
|
||||
js_free(data);
|
||||
if (out.count()) {
|
||||
out.discardTransferables(callbacks, closure);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1334,12 +1439,12 @@ JSStructuredCloneWriter::transferOwnership()
|
||||
// Walk along the transferables and the transfer map at the same time,
|
||||
// grabbing out pointers from the transferables and stuffing them into the
|
||||
// transfer map.
|
||||
uint64_t* point = out.rawBuffer();
|
||||
MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_HEADER);
|
||||
auto point = out.iter();
|
||||
MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_HEADER);
|
||||
point++;
|
||||
MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
|
||||
MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
|
||||
point++;
|
||||
MOZ_ASSERT(LittleEndian::readUint64(point) == transferableObjects.count());
|
||||
MOZ_ASSERT(NativeEndian::swapFromLittleEndian(point.peek()) == transferableObjects.count());
|
||||
point++;
|
||||
|
||||
RootedObject obj(context());
|
||||
@ -1352,7 +1457,7 @@ JSStructuredCloneWriter::transferOwnership()
|
||||
uint64_t extraData;
|
||||
|
||||
#if DEBUG
|
||||
SCInput::getPair(point, &tag, (uint32_t*) &ownership);
|
||||
SCInput::getPair(point.peek(), &tag, (uint32_t*) &ownership);
|
||||
MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY);
|
||||
MOZ_ASSERT(ownership == JS::SCTAG_TMO_UNFILLED);
|
||||
#endif
|
||||
@ -1407,22 +1512,23 @@ JSStructuredCloneWriter::transferOwnership()
|
||||
MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
|
||||
}
|
||||
|
||||
LittleEndian::writeUint64(point++, PairToUInt64(tag, ownership));
|
||||
LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(content));
|
||||
LittleEndian::writeUint64(point++, extraData);
|
||||
point.write(NativeEndian::swapToLittleEndian(PairToUInt64(tag, ownership)));
|
||||
point.next();
|
||||
point.write(NativeEndian::swapToLittleEndian(reinterpret_cast<uint64_t>(content)));
|
||||
point.next();
|
||||
point.write(NativeEndian::swapToLittleEndian(extraData));
|
||||
point.next();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(point <= out.rawBuffer() + out.count());
|
||||
#if DEBUG
|
||||
// Make sure there aren't any more transfer map entries after the expected
|
||||
// number we read out.
|
||||
if (point < out.rawBuffer() + out.count()) {
|
||||
if (!point.done()) {
|
||||
uint32_t tag, data;
|
||||
SCInput::getPair(point, &tag, &data);
|
||||
SCInput::getPair(point.peek(), &tag, &data);
|
||||
MOZ_ASSERT(tag < SCTAG_TRANSFER_MAP_HEADER || tag >= SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1955,7 +2061,7 @@ bool
|
||||
JSStructuredCloneReader::readTransferMap()
|
||||
{
|
||||
JSContext* cx = context();
|
||||
uint64_t* headerPos = in.tell();
|
||||
auto headerPos = in.tell();
|
||||
|
||||
uint32_t tag, data;
|
||||
if (!in.getPair(&tag, &data))
|
||||
@ -1970,7 +2076,7 @@ JSStructuredCloneReader::readTransferMap()
|
||||
return false;
|
||||
|
||||
for (uint64_t i = 0; i < numTransferables; i++) {
|
||||
uint64_t* pos = in.tell();
|
||||
auto pos = in.tell();
|
||||
|
||||
if (!in.readPair(&tag, &data))
|
||||
return false;
|
||||
@ -2026,21 +2132,20 @@ JSStructuredCloneReader::readTransferMap()
|
||||
|
||||
// Mark the SCTAG_TRANSFER_MAP_* entry as no longer owned by the input
|
||||
// buffer.
|
||||
*pos = PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED);
|
||||
MOZ_ASSERT(headerPos < pos && pos < in.end());
|
||||
pos.write(PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED));
|
||||
MOZ_ASSERT(!pos.done());
|
||||
|
||||
if (!allObjs.append(ObjectValue(*obj)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark the whole transfer map as consumed.
|
||||
MOZ_ASSERT(headerPos <= in.tell());
|
||||
#ifdef DEBUG
|
||||
SCInput::getPair(headerPos, &tag, &data);
|
||||
SCInput::getPair(headerPos.peek(), &tag, &data);
|
||||
MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_HEADER);
|
||||
MOZ_ASSERT(TransferableMapHeader(data) != SCTAG_TM_TRANSFERRED);
|
||||
#endif
|
||||
*headerPos = PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED);
|
||||
headerPos.write(PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2235,7 +2340,7 @@ JSStructuredCloneReader::read(MutableHandleValue vp)
|
||||
using namespace js;
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ReadStructuredClone(JSContext* cx, uint64_t* buf, size_t nbytes,
|
||||
JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& buf,
|
||||
uint32_t version, JS::StructuredCloneScope scope,
|
||||
MutableHandleValue vp,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
@ -2249,11 +2354,11 @@ JS_ReadStructuredClone(JSContext* cx, uint64_t* buf, size_t nbytes,
|
||||
return false;
|
||||
}
|
||||
const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
|
||||
return ReadStructuredClone(cx, buf, nbytes, scope, vp, callbacks, closure);
|
||||
return ReadStructuredClone(cx, buf, scope, vp, callbacks, closure);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_WriteStructuredClone(JSContext* cx, HandleValue value, uint64_t** bufp, size_t* nbytesp,
|
||||
JS_WriteStructuredClone(JSContext* cx, HandleValue value, JSStructuredCloneData* bufp,
|
||||
JS::StructuredCloneScope scope,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void* closure, HandleValue transferable)
|
||||
@ -2263,26 +2368,14 @@ JS_WriteStructuredClone(JSContext* cx, HandleValue value, uint64_t** bufp, size_
|
||||
assertSameCompartment(cx, value);
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
|
||||
return WriteStructuredClone(cx, value, bufp, nbytesp, scope, callbacks, closure, transferable);
|
||||
return WriteStructuredClone(cx, value, bufp, scope, callbacks, closure, transferable);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void* closure, bool freeData)
|
||||
{
|
||||
DiscardTransferables(data, nbytes, optionalCallbacks, closure);
|
||||
if (freeData) {
|
||||
js_free(data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes,
|
||||
JS_StructuredCloneHasTransferables(JSStructuredCloneData& data,
|
||||
bool* hasTransferable)
|
||||
{
|
||||
*hasTransferable = StructuredCloneHasTransferObjects(data, nbytes);
|
||||
*hasTransferable = StructuredCloneHasTransferObjects(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2328,8 +2421,8 @@ JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp,
|
||||
JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
|
||||
: scope_(other.scope_)
|
||||
{
|
||||
ownTransferables_ = other.ownTransferables_;
|
||||
other.steal(&data_, &nbytes_, &version_, &callbacks_, &closure_);
|
||||
data_.ownTransferables_ = other.data_.ownTransferables_;
|
||||
other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
|
||||
}
|
||||
|
||||
JSAutoStructuredCloneBuffer&
|
||||
@ -2338,8 +2431,8 @@ JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
|
||||
MOZ_ASSERT(&other != this);
|
||||
MOZ_ASSERT(scope_ == other.scope_);
|
||||
clear();
|
||||
ownTransferables_ = other.ownTransferables_;
|
||||
other.steal(&data_, &nbytes_, &version_, &callbacks_, &closure_);
|
||||
data_.ownTransferables_ = other.data_.ownTransferables_;
|
||||
other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -2347,81 +2440,68 @@ void
|
||||
JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void* optionalClosure)
|
||||
{
|
||||
if (!data_)
|
||||
if (!data_.Size())
|
||||
return;
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
optionalCallbacks ? optionalCallbacks : callbacks_;
|
||||
void* closure = optionalClosure ? optionalClosure : closure_;
|
||||
optionalCallbacks ? optionalCallbacks : data_.callbacks_;
|
||||
void* closure = optionalClosure ? optionalClosure : data_.closure_;
|
||||
|
||||
if (ownTransferables_ == OwnsTransferablesIfAny)
|
||||
DiscardTransferables(data_, nbytes_, callbacks, closure);
|
||||
ownTransferables_ = NoTransferables;
|
||||
js_free(data_);
|
||||
data_ = nullptr;
|
||||
nbytes_ = 0;
|
||||
if (data_.ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
|
||||
DiscardTransferables(data_, callbacks, closure);
|
||||
data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
|
||||
data_.Clear();
|
||||
version_ = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
JSAutoStructuredCloneBuffer::copy(const uint64_t* srcData, size_t nbytes, uint32_t version,
|
||||
JSAutoStructuredCloneBuffer::copy(const JSStructuredCloneData& srcData, uint32_t version,
|
||||
const JSStructuredCloneCallbacks* callbacks,
|
||||
void* closure)
|
||||
{
|
||||
// transferable objects cannot be copied
|
||||
if (StructuredCloneHasTransferObjects(data_, nbytes_))
|
||||
if (StructuredCloneHasTransferObjects(srcData))
|
||||
return false;
|
||||
|
||||
uint64_t* newData = static_cast<uint64_t*>(js_malloc(nbytes));
|
||||
if (!newData)
|
||||
return false;
|
||||
|
||||
js_memcpy(newData, srcData, nbytes);
|
||||
|
||||
clear();
|
||||
data_ = newData;
|
||||
nbytes_ = nbytes;
|
||||
|
||||
auto iter = srcData.Iter();
|
||||
while (!iter.Done()) {
|
||||
data_.WriteBytes(iter.Data(), iter.RemainingInSegment());
|
||||
iter.Advance(srcData, iter.RemainingInSegment());
|
||||
}
|
||||
|
||||
version_ = version;
|
||||
callbacks_ = callbacks;
|
||||
closure_ = closure;
|
||||
ownTransferables_ = NoTransferables;
|
||||
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JSAutoStructuredCloneBuffer::adopt(uint64_t* data, size_t nbytes, uint32_t version,
|
||||
JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t version,
|
||||
const JSStructuredCloneCallbacks* callbacks,
|
||||
void* closure)
|
||||
{
|
||||
clear();
|
||||
data_ = data;
|
||||
nbytes_ = nbytes;
|
||||
data_ = Move(data);
|
||||
version_ = version;
|
||||
callbacks_ = callbacks;
|
||||
closure_ = closure;
|
||||
ownTransferables_ = OwnsTransferablesIfAny;
|
||||
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny);
|
||||
}
|
||||
|
||||
void
|
||||
JSAutoStructuredCloneBuffer::steal(uint64_t** datap, size_t* nbytesp, uint32_t* versionp,
|
||||
JSAutoStructuredCloneBuffer::steal(JSStructuredCloneData* data, uint32_t* versionp,
|
||||
const JSStructuredCloneCallbacks** callbacks,
|
||||
void** closure)
|
||||
{
|
||||
*datap = data_;
|
||||
*nbytesp = nbytes_;
|
||||
if (versionp)
|
||||
*versionp = version_;
|
||||
if (callbacks)
|
||||
*callbacks = callbacks_;
|
||||
*callbacks = data_.callbacks_;
|
||||
if (closure)
|
||||
*closure = closure_;
|
||||
*closure = data_.closure_;
|
||||
*data = Move(data_);
|
||||
|
||||
data_ = nullptr;
|
||||
nbytes_ = 0;
|
||||
version_ = 0;
|
||||
callbacks_ = 0;
|
||||
closure_ = 0;
|
||||
ownTransferables_ = NoTransferables;
|
||||
data_.setOptionalCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2430,8 +2510,7 @@ JSAutoStructuredCloneBuffer::read(JSContext* cx, MutableHandleValue vp,
|
||||
void* closure)
|
||||
{
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(data_);
|
||||
return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, scope_, vp,
|
||||
return !!JS_ReadStructuredClone(cx, data_, version_, scope_, vp,
|
||||
optionalCallbacks, closure);
|
||||
}
|
||||
|
||||
@ -2451,18 +2530,16 @@ JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
|
||||
void* closure)
|
||||
{
|
||||
clear();
|
||||
bool ok = JS_WriteStructuredClone(cx, value, &data_, &nbytes_,
|
||||
bool ok = JS_WriteStructuredClone(cx, value, &data_,
|
||||
scope_,
|
||||
optionalCallbacks, closure,
|
||||
transferable);
|
||||
|
||||
if (ok) {
|
||||
ownTransferables_ = OwnsTransferablesIfAny;
|
||||
data_.ownTransferables_ = OwnTransferablePolicy::OwnsTransferablesIfAny;
|
||||
} else {
|
||||
data_ = nullptr;
|
||||
nbytes_ = 0;
|
||||
version_ = JS_STRUCTURED_CLONE_VERSION;
|
||||
ownTransferables_ = NoTransferables;
|
||||
data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ class BufferList : private AllocPolicy
|
||||
AllocPolicy aAP = AllocPolicy())
|
||||
: AllocPolicy(aAP),
|
||||
mOwning(true),
|
||||
mSegments(aAP),
|
||||
mSize(0),
|
||||
mStandardCapacity(aStandardCapacity)
|
||||
{
|
||||
@ -259,7 +260,7 @@ class BufferList : private AllocPolicy
|
||||
// AllocPolicy is only used for the buffer vector.
|
||||
template<typename BorrowingAllocPolicy>
|
||||
BufferList<BorrowingAllocPolicy> Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
|
||||
BorrowingAllocPolicy aAP = BorrowingAllocPolicy());
|
||||
BorrowingAllocPolicy aAP = BorrowingAllocPolicy()) const;
|
||||
|
||||
// Return a new BufferList and move storage from this BufferList to it. The
|
||||
// new BufferList owns the buffers. Move can fail, in which case *aSuccess
|
||||
@ -432,7 +433,7 @@ BufferList<AllocPolicy>::FlattenBytes(IterImpl& aIter, const char** aOutData, si
|
||||
template<typename AllocPolicy> template<typename BorrowingAllocPolicy>
|
||||
BufferList<BorrowingAllocPolicy>
|
||||
BufferList<AllocPolicy>::Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
|
||||
BorrowingAllocPolicy aAP)
|
||||
BorrowingAllocPolicy aAP) const
|
||||
{
|
||||
BufferList<BorrowingAllocPolicy> result(aAP);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user