--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -128,27 +128,27 @@ StructuredCloneCallbacksFreeTransfer(uin
void
StructuredCloneCallbacksError(JSContext* aCx,
uint32_t aErrorId)
{
NS_WARNING("Failed to clone data.");
}
-const JSStructuredCloneCallbacks gCallbacks = {
+} // anonymous namespace
+
+const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
StructuredCloneCallbacksRead,
StructuredCloneCallbacksWrite,
StructuredCloneCallbacksError,
StructuredCloneCallbacksReadTransfer,
StructuredCloneCallbacksWriteTransfer,
StructuredCloneCallbacksFreeTransfer
};
-} // anonymous namespace
-
// StructuredCloneHolderBase class
StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope)
: mStructuredCloneScope(aScope)
#ifdef DEBUG
, mClearCalled(false)
#endif
{}
@@ -180,34 +180,34 @@ StructuredCloneHolderBase::Write(JSConte
bool
StructuredCloneHolderBase::Write(JSContext* aCx,
JS::Handle<JS::Value> aValue,
JS::Handle<JS::Value> aTransfer)
{
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;
}
return true;
}
bool
StructuredCloneHolderBase::Read(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue)
{
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;
}
bool
StructuredCloneHolderBase::CustomReadTransferHandler(JSContext* aCx,
JSStructuredCloneReader* aReader,
uint32_t aTag,
void* aContent,
@@ -306,85 +306,48 @@ StructuredCloneHolder::Read(nsISupports*
mClonedSurfaces.Clear();
Clear();
}
}
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)
{
MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
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,
uint32_t aTag)
{
if (aTag == SCTAG_DOM_IMAGEDATA) {
return ReadStructuredCloneImageData(aCx, aReader);
}
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -3,17 +3,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_StructuredCloneHolder_h
#define mozilla_dom_StructuredCloneHolder_h
#include "js/StructuredClone.h"
#include "mozilla/Move.h"
-#include "nsAutoPtr.h"
+#include "mozilla/UniquePtr.h"
#include "nsISupports.h"
#include "nsTArray.h"
#ifdef DEBUG
#include "nsIThread.h"
#endif
namespace mozilla {
@@ -97,30 +97,24 @@ public:
bool Read(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue);
bool HasData() const
{
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;
#ifdef DEBUG
bool mClearCalled;
#endif
};
@@ -167,22 +161,16 @@ public:
JS::Handle<JS::Value> aTransfer,
ErrorResult &aRv);
void Read(nsISupports* aParent,
JSContext* aCx,
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
{
return !mBlobImplArray.IsEmpty() ||
!mClonedSurfaces.IsEmpty();
}
nsTArray<RefPtr<BlobImpl>>& BlobImpls()
@@ -261,39 +249,35 @@ public:
static JSObject* ReadFullySerializableObjects(JSContext* aCx,
JSStructuredCloneReader* aReader,
uint32_t aTag);
static bool WriteFullySerializableObjects(JSContext* aCx,
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;
// Used for cloning blobs in the structured cloning algorithm.
nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
// This is used for sharing the backend of ImageBitmaps.
// The DataSourceSurface object must be thread-safely reference-counted.
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -271,18 +271,23 @@ struct DataBlobs<Child>
template<ActorFlavorEnum Flavor>
static bool
BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType* aManager,
StructuredCloneData& aData,
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();
if (!blobImpls.IsEmpty()) {
typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData);
uint32_t length = blobImpls.Length();
@@ -320,17 +325,17 @@ static void
UnpackClonedMessageData(const ClonedMessageData& aClonedData,
StructuredCloneData& aData)
{
const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
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);
if (!blobs.IsEmpty()) {
uint32_t length = blobs.Length();
aData.BlobImpls().SetCapacity(length);
for (uint32_t i = 0; i < length; ++i) {
auto* blob =
--- a/dom/base/nsStructuredCloneContainer.cpp
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -132,17 +132,21 @@ nsStructuredCloneContainer::GetDataAsBas
if (!DataLength()) {
return NS_ERROR_FAILURE;
}
if (HasClonedDOMObjects()) {
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))) {
return rv;
}
CopyASCIItoUTF16(base64Data, aOut);
return NS_OK;
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -147,19 +147,23 @@ public:
{
MOZ_ASSERT(mActor);
if (mActor->IsActorDestroyed()) {
return NS_OK;
}
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);
const nsTArray<RefPtr<BlobImpl>>& blobImpls = mData->BlobImpls();
if (!blobImpls.IsEmpty()) {
message.blobsChild().SetCapacity(blobImpls.Length());
--- a/dom/broadcastchannel/BroadcastChannelChild.cpp
+++ b/dom/broadcastchannel/BroadcastChannelChild.cpp
@@ -86,22 +86,21 @@ BroadcastChannelChild::RecvNotify(const
NS_WARNING("Failed to initialize AutoJSAPI object.");
return true;
}
ipc::StructuredCloneData cloneData;
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();
return true;
}
}
RootedDictionary<MessageEventInit> init(cx);
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -866,17 +866,17 @@ CommonStructuredCloneReadCallback(JSCont
return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader,
aTag);
}
// static
void
ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
{
- if (aBuffer.data()) {
+ if (!aBuffer.empty()) {
aBuffer.clear();
}
}
} // namespace
const JSClass IDBObjectStore::sDummyPropJSClass = {
"IDBObjectStore Dummy",
@@ -1037,17 +1037,17 @@ IDBObjectStore::AppendIndexUpdateInfo(
// static
void
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;
}
ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer);
aReadInfo.mFiles.Clear();
}
// static
@@ -1058,35 +1058,40 @@ IDBObjectStore::DeserializeValue(JSConte
{
MOZ_ASSERT(aCx);
if (aCloneReadInfo.mData.IsEmpty()) {
aValue.setUndefined();
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,
nullptr,
nullptr,
nullptr,
nullptr
};
// 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;
}
return true;
}
@@ -1100,32 +1105,34 @@ IDBObjectStore::DeserializeIndexValue(JS
MOZ_ASSERT(aCx);
if (aCloneReadInfo.mData.IsEmpty()) {
aValue.setUndefined();
return true;
}
size_t dataLen = aCloneReadInfo.mData.Length();
-
- uint64_t* data =
- const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
- aCloneReadInfo.mData.Elements()));
-
- MOZ_ASSERT(!(dataLen % sizeof(*data)));
+ char* data = reinterpret_cast<char*>(aCloneReadInfo.mData.Elements());
+
+ 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;
}
return true;
}
@@ -1140,36 +1147,39 @@ IDBObjectStore::DeserializeUpgradeValue(
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aCx);
if (aCloneReadInfo.mData.IsEmpty()) {
aValue.setUndefined();
return true;
}
+
size_t dataLen = aCloneReadInfo.mData.Length();
-
- uint64_t* data =
- const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
- aCloneReadInfo.mData.Elements()));
-
- MOZ_ASSERT(!(dataLen % sizeof(*data)));
+ char* data = reinterpret_cast<char*>(aCloneReadInfo.mData.Elements());
+
+ MOZ_ASSERT(!(dataLen % sizeof(uint64_t)));
JSAutoRequest ar(aCx);
+ JSStructuredCloneData buf;
+ if (!buf.WriteBytes(data, dataLen)) {
+ return false;
+ }
+
static JSStructuredCloneCallbacks callbacks = {
CommonStructuredCloneReadCallback<UpgradeDeserializationHelper>,
nullptr,
nullptr,
nullptr,
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;
}
return true;
}
@@ -1292,25 +1302,31 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
nsTArray<IndexUpdateInfo> updateInfo;
aRv = GetAddInfo(aCx, value, aKey, cloneWriteInfo, key, updateInfo);
if (aRv.Failed()) {
return nullptr;
}
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();
ObjectStoreAddPutParams commonParams;
commonParams.objectStoreId() = Id();
commonParams.cloneInfo().data().SwapElements(cloneData);
commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp;
commonParams.key() = key;
--- a/dom/indexedDB/IndexedDatabaseInlines.h
+++ b/dom/indexedDB/IndexedDatabaseInlines.h
@@ -42,29 +42,25 @@ StructuredCloneFile::operator==(const St
this->mMutableFile == aOther.mMutableFile &&
this->mFileInfo == aOther.mFileInfo &&
this->mMutable == aOther.mMutable;
}
inline
StructuredCloneReadInfo::StructuredCloneReadInfo()
: mDatabase(nullptr)
- , mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
- nullptr)
{
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
}
inline
StructuredCloneReadInfo::StructuredCloneReadInfo(
SerializedStructuredCloneReadInfo&& aCloneReadInfo)
: mData(Move(aCloneReadInfo.data()))
, mDatabase(nullptr)
- , mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
- nullptr)
{
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
}
inline
StructuredCloneReadInfo::~StructuredCloneReadInfo()
{
MOZ_COUNT_DTOR(StructuredCloneReadInfo);
--- a/dom/ipc/StructuredCloneData.cpp
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -23,122 +23,109 @@
namespace mozilla {
namespace dom {
namespace ipc {
bool
StructuredCloneData::Copy(const StructuredCloneData& aData)
{
- if (!aData.Data()) {
+ if (!aData.mInitialized) {
return true;
}
if (aData.SharedData()) {
mSharedData = aData.SharedData();
} else {
mSharedData =
- SharedJSAllocatedData::CreateFromExternalData(aData.Data(),
- aData.DataLength());
+ SharedJSAllocatedData::CreateFromExternalData(aData.Data());
NS_ENSURE_TRUE(mSharedData, false);
}
PortIdentifiers().AppendElements(aData.PortIdentifiers());
MOZ_ASSERT(BlobImpls().IsEmpty());
BlobImpls().AppendElements(aData.BlobImpls());
MOZ_ASSERT(GetSurfaces().IsEmpty());
+ mInitialized = true;
+
return true;
}
void
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
StructuredCloneData::Write(JSContext* aCx,
JS::Handle<JS::Value> aValue,
ErrorResult &aRv)
{
Write(aCx, aValue, JS::UndefinedHandleValue, aRv);
}
void
StructuredCloneData::Write(JSContext* aCx,
JS::Handle<JS::Value> aValue,
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;
}
} // namespace ipc
} // namespace dom
} // namespace mozilla
--- a/dom/ipc/StructuredCloneData.h
+++ b/dom/ipc/StructuredCloneData.h
@@ -19,83 +19,69 @@ class PickleIterator;
namespace mozilla {
namespace dom {
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
{
public:
StructuredCloneData()
: 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;
const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
{
return mBlobImplArray;
}
@@ -115,48 +101,55 @@ public:
JS::Handle<JS::Value> aValue,
ErrorResult &aRv);
void Write(JSContext* aCx,
JS::Handle<JS::Value> aValue,
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
{
return mSharedData;
}
// For IPC serialization
void WriteIPCParams(IPC::Message* aMessage) const;
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
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ipc_StructuredCloneData_h
--- a/dom/messagechannel/PMessagePort.ipdl
+++ b/dom/messagechannel/PMessagePort.ipdl
@@ -2,24 +2,28 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PBackground;
include protocol PBlob;
include DOMTypes;
+using struct mozilla::SerializedStructuredCloneBuffer
+ from "ipc/IPCMessageUtils.h";
+
namespace mozilla {
namespace dom {
+
struct MessagePortMessage
{
+ SerializedStructuredCloneBuffer data;
+ PBlob[] blobs;
MessagePortIdentifier[] transferredPorts;
- uint8_t[] data;
- PBlob[] blobs;
};
// This protocol is used for the MessageChannel/MessagePort API
protocol PMessagePort
{
manager PBackground;
/* Many of these methods are used just for the shutdown sequence. The
--- a/dom/messagechannel/SharedMessagePortMessage.cpp
+++ b/dom/messagechannel/SharedMessagePortMessage.cpp
@@ -15,92 +15,33 @@
#include "mozilla/ipc/BackgroundParent.h"
namespace mozilla {
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,
const nsTArray<RefPtr<SharedMessagePortMessage>>& aData,
nsTArray<MessagePortMessage>& aArray)
{
MOZ_ASSERT(aActor);
MOZ_ASSERT(aArray.IsEmpty());
aArray.SetCapacity(aData.Length());
PBackgroundChild* backgroundManager = aActor->Manager();
MOZ_ASSERT(backgroundManager);
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()) {
message->blobsChild().SetCapacity(blobImpls.Length());
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
PBlobChild* blobChild =
BackgroundChild::GetOrCreateActorForBlobImpl(backgroundManager,
@@ -122,17 +63,20 @@ SharedMessagePortMessage::FromMessagesTo
if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
return false;
}
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()) {
data->BlobImpls().SetCapacity(blobs.Length());
for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
RefPtr<BlobImpl> impl =
static_cast<BlobChild*>(blobs[i])->GetBlobImpl();
@@ -162,17 +106,18 @@ SharedMessagePortMessage::FromSharedToMe
return false;
}
PBackgroundParent* backgroundManager = aActor->Manager();
MOZ_ASSERT(backgroundManager);
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()) {
message->blobsParent().SetCapacity(blobImpls.Length());
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
PBlobParent* blobParent =
BackgroundParent::GetOrCreateActorForBlobImpl(backgroundManager,
@@ -196,17 +141,20 @@ SharedMessagePortMessage::FromMessagesTo
if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
return false;
}
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()) {
data->BlobImpls().SetCapacity(blobs.Length());
for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
RefPtr<BlobImpl> impl =
static_cast<BlobParent*>(blobs[i])->GetBlobImpl();
--- a/dom/messagechannel/SharedMessagePortMessage.h
+++ b/dom/messagechannel/SharedMessagePortMessage.h
@@ -15,35 +15,21 @@ class MessagePortChild;
class MessagePortMessage;
class MessagePortParent;
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,
const nsTArray<RefPtr<SharedMessagePortMessage>>& aData,
nsTArray<MessagePortMessage>& aArray);
static bool
FromMessagesToSharedChild(
@@ -57,15 +43,15 @@ public:
FallibleTArray<MessagePortMessage>& aArray);
static bool
FromMessagesToSharedParent(
nsTArray<MessagePortMessage>& aArray,
FallibleTArray<RefPtr<SharedMessagePortMessage>>& aData);
private:
- ~SharedMessagePortMessage();
+ ~SharedMessagePortMessage() {}
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_SharedMessagePortMessage_h
--- a/ipc/chromium/src/base/pickle.cc
+++ b/ipc/chromium/src/base/pickle.cc
@@ -416,22 +416,33 @@ bool Pickle::FlattenBytes(PickleIterator
return false;
}
header_ = reinterpret_cast<Header*>(buffers_.Start());
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) {
return false;
}
return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
}
--- a/ipc/chromium/src/base/pickle.h
+++ b/ipc/chromium/src/base/pickle.h
@@ -108,17 +108,18 @@ class Pickle {
MOZ_MUST_USE bool ReadDouble(PickleIterator* iter, double* result) const;
MOZ_MUST_USE bool ReadIntPtr(PickleIterator* iter, intptr_t* result) const;
MOZ_MUST_USE bool ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const;
MOZ_MUST_USE bool ReadString(PickleIterator* iter, std::string* result) const;
MOZ_MUST_USE bool ReadWString(PickleIterator* iter, std::wstring* result) const;
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.
MOZ_MUST_USE bool ReadLength(PickleIterator* iter, int* result) const;
MOZ_MUST_USE bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const
#ifdef MOZ_PICKLE_SENTINEL_CHECKING
;
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -59,31 +59,41 @@ typedef uintptr_t WindowsHandle;
// move to nscore.h or something.
struct void_t {
bool operator==(const void_t&) const { return true; }
};
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
namespace IPC {
/**
* Maximum size, in bytes, of a single IPC message.
@@ -699,54 +709,82 @@ struct ParamTraits<mozilla::net::WebSock
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
return aResult->ReadIPCParams(aMsg, aIter);
}
};
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>
{
typedef mozilla::SerializedStructuredCloneBuffer paramType;
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);
}
};
template <>
struct ParamTraits<nsIWidget::TouchPointerState>
: public BitFlagsEnumSerializer<nsIWidget::TouchPointerState,
nsIWidget::TouchPointerState::ALL_BITS>
{
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -2,16 +2,18 @@
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef js_StructuredClone_h
#define js_StructuredClone_h
+#include "mozilla/BufferList.h"
+
#include <stdint.h>
#include "jstypes.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
#include "js/Value.h"
@@ -125,19 +127,19 @@ typedef bool (*TransferStructuredCloneOp
void* closure,
// Output:
uint32_t* tag,
JS::TransferableOwnership* ownership,
void** content,
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);
// The maximum supported structured-clone serialization format version.
// Increment this when anything at all changes in the serialization format.
// (Note that this does not need to be bumped for Transferable-only changes,
// since they are never saved to persistent storage.)
@@ -147,105 +149,135 @@ struct JSStructuredCloneCallbacks {
ReadStructuredCloneOp read;
WriteStructuredCloneOp write;
StructuredCloneErrorOp reportError;
ReadTransferStructuredCloneOp readTransfer;
TransferStructuredCloneOp writeTransfer;
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,
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
/** 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);
/**
* Adopt some memory. It will be automatically freed by the destructor.
* 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);
bool write(JSContext* cx, JS::HandleValue v,
const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable,
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2070,46 +2070,41 @@ class CloneBufferObject : public NativeO
return &obj->as<CloneBufferObject>();
}
static CloneBufferObject* Create(JSContext* cx, JSAutoStructuredCloneBuffer* buffer) {
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));
}
static bool
setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
if (args.length() != 1 || !args[0].isString()) {
JS_ReportError(cx,
"the first argument argument must be maxBytes, "
@@ -2126,18 +2121,22 @@ class CloneBufferObject : public NativeO
}
Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
obj->discard();
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;
}
static bool
is(HandleValue v) {
return v.isObject() && v.toObject().is<CloneBufferObject>();
@@ -2155,25 +2154,29 @@ class CloneBufferObject : public NativeO
MOZ_ASSERT(args.length() == 0);
if (!obj->data()) {
args.rval().setUndefined();
return true;
}
bool hasTransferable;
- if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
+ if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
return false;
if (hasTransferable) {
JS_ReportError(cx, "cannot retrieve structured clone buffer with transferables");
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);
return true;
}
static bool
getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
@@ -2244,25 +2247,24 @@ Deserialize(JSContext* cx, unsigned argc
// Clone buffer was already consumed?
if (!obj->data()) {
JS_ReportError(cx, "deserialize given invalid clone buffer "
"(transferables already consumed?)");
return false;
}
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);
if (hasTransferable)
obj->discard();
return true;
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -147,16 +147,70 @@ enum TransferableMapHeader {
static inline uint64_t
PairToUInt64(uint32_t tag, uint32_t data)
{
return uint64_t(data) | (uint64_t(tag) << 32);
}
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);
JSContext* context() const { return cx; }
bool write(uint64_t u);
bool writePair(uint32_t tag, uint32_t data);
@@ -164,49 +218,53 @@ struct SCOutput {
bool writeBytes(const void* p, size_t nbytes);
bool writeChars(const Latin1Char* p, size_t nchars);
bool writeChars(const char16_t* p, size_t nchars);
bool writePtr(const void*);
template <class T>
bool writeArray(const T* p, size_t nbytes);
- bool extractBuffer(uint64_t** datap, size_t* sizep);
-
- uint64_t count() const { return buf.length(); }
- uint64_t* rawBuffer() { return buf.begin(); }
+ bool extractBuffer(JSStructuredCloneData* data);
+ void discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure);
+
+ 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);
bool readPair(uint32_t* tagp, uint32_t* datap);
bool readDouble(double* p);
bool readBytes(void* p, size_t nbytes);
bool readChars(Latin1Char* p, size_t nchars);
bool readChars(char16_t* p, size_t nchars);
bool readPtr(void**);
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);
bool reportTruncated() {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "truncated");
return false;
@@ -214,18 +272,17 @@ class SCInput {
private:
void staticAssertions() {
JS_STATIC_ASSERT(sizeof(char16_t) == 2);
JS_STATIC_ASSERT(sizeof(uint32_t) == 4);
}
JSContext* cx;
- uint64_t* point;
- uint64_t* bufEnd;
+ BufferIterator point;
};
} /* namespace js */
struct JSStructuredCloneReader {
public:
explicit JSStructuredCloneReader(SCInput& in, JS::StructuredCloneScope scope,
const JSStructuredCloneCallbacks* cb,
@@ -297,18 +354,23 @@ struct JSStructuredCloneWriter {
}
return parseTransferable() && writeHeader() && writeTransferMap();
}
bool write(HandleValue v);
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; }
private:
JSStructuredCloneWriter() = delete;
JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
@@ -417,87 +479,92 @@ ReportDataCloneError(JSContext* cx,
default:
MOZ_CRASH("Unkown errorId");
break;
}
}
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);
}
// 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)
return;
if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
return;
// 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;
if (ownership == JS::SCTAG_TMO_ALLOC_DATA) {
js_free(content);
} else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) {
JS_ReleaseMappedArrayBufferContents(content, extraData);
@@ -509,56 +576,61 @@ DiscardTransferables(uint64_t* buffer, s
cb->freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, cbClosure);
} else {
MOZ_ASSERT(false, "unknown ownership");
}
}
}
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;
}
bool
SCInput::readPair(uint32_t* tagp, uint32_t* datap)
{
uint64_t u;
bool ok = read(&u);
@@ -567,38 +639,38 @@ SCInput::readPair(uint32_t* tagp, uint32
*datap = uint32_t(u);
}
return ok;
}
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;
}
bool
SCInput::getPair(uint32_t* tagp, uint32_t* datap)
{
uint64_t u = 0;
if (!get(&u))
return false;
*tagp = uint32_t(u >> 32);
*datap = uint32_t(u);
return true;
}
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);
}
bool
SCInput::readDouble(double* p)
{
union {
@@ -608,45 +680,52 @@ SCInput::readDouble(double* p)
if (!read(&pun.u))
return false;
*p = CanonicalizeNaN(pun.d);
return true;
}
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);
/*
* Fail if nelems is so huge as to make JS_HOWMANY overflow or if nwords is
* 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;
}
bool
SCInput::readBytes(void* p, size_t nbytes)
{
return readArray((uint8_t*) p, nbytes);
}
@@ -661,39 +740,44 @@ SCInput::readChars(Latin1Char* p, size_t
bool
SCInput::readChars(char16_t* p, size_t nchars)
{
MOZ_ASSERT(sizeof(char16_t) == sizeof(uint16_t));
return readArray((uint16_t*) p, 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
SCInput::readPtr(void** p)
{
uint64_t u;
if (!readNativeEndian(&u))
return false;
*p = reinterpret_cast<void*>(NativeEndian::swapFromLittleEndian(u));
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
SCOutput::writePair(uint32_t tag, uint32_t data)
{
/*
* As it happens, the tag word appears after the data word in the output.
* This is because exponents occupy the last 2 bytes of doubles on the
@@ -714,53 +798,59 @@ ReinterpretPairAsDouble(uint32_t tag, ui
bool
SCOutput::writeDouble(double d)
{
return write(BitwiseCast<uint64_t>(CanonicalizeNaN(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;
if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < 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;
-
- buf.back() = 0; /* zero-pad to an 8-byte boundary */
-
- T* q = (T*) &buf[start];
- copyAndSwapToLittleEndian(q, p, nelems);
+ 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;
+ }
+
return true;
}
bool
SCOutput::writeBytes(const void* p, size_t nbytes)
{
return writeArray((const uint8_t*) p, nbytes);
}
@@ -783,37 +873,52 @@ SCOutput::writeChars(const Latin1Char* p
bool
SCOutput::writePtr(const void* p)
{
return write(reinterpret_cast<uint64_t>(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);
}
}
bool
JSStructuredCloneWriter::parseTransferable()
{
// NOTE: The transferables set is tested for non-emptiness at various
// junctures in structured cloning, so this set must be initialized
@@ -1329,35 +1434,35 @@ bool
JSStructuredCloneWriter::transferOwnership()
{
if (transferableObjects.empty())
return true;
// 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());
for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
obj = tr.front();
uint32_t tag;
JS::TransferableOwnership ownership;
void* content;
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
ESClass cls;
if (!GetBuiltinClass(context(), obj, &cls))
return false;
@@ -1402,32 +1507,33 @@ JSStructuredCloneWriter::transferOwnersh
} else {
if (!callbacks || !callbacks->writeTransfer)
return reportDataCloneError(JS_SCERR_TRANSFERABLE);
if (!callbacks->writeTransfer(context(), obj, closure, &tag, &ownership, &content, &extraData))
return false;
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;
}
bool
JSStructuredCloneWriter::write(HandleValue v)
{
if (!startWrite(v))
return false;
@@ -1950,32 +2056,32 @@ JSStructuredCloneReader::readHeader()
return true;
}
bool
JSStructuredCloneReader::readTransferMap()
{
JSContext* cx = context();
- uint64_t* headerPos = in.tell();
+ auto headerPos = in.tell();
uint32_t tag, data;
if (!in.getPair(&tag, &data))
return in.reportTruncated();
if (tag != SCTAG_TRANSFER_MAP_HEADER || TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
return true;
uint64_t numTransferables;
MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
if (!in.read(&numTransferables))
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;
MOZ_ASSERT(tag != SCTAG_TRANSFER_MAP_PENDING_ENTRY);
RootedObject obj(cx);
void* content;
@@ -2021,31 +2127,30 @@ JSStructuredCloneReader::readTransferMap
// On failure, the buffer will still own the data (since its ownership
// will not get set to SCTAG_TMO_UNOWNED), so the data will be freed by
// DiscardTransferables.
if (!obj)
return false;
// 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;
}
JSObject*
JSStructuredCloneReader::readSavedFrame(uint32_t principalsTag)
{
RootedSavedFrame savedFrame(context(), SavedFrame::create(context()));
@@ -2230,64 +2335,52 @@ JSStructuredCloneReader::read(MutableHan
allObjs.clear();
return true;
}
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,
void* closure)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
if (version > JS_STRUCTURED_CLONE_VERSION) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SC_BAD_CLONE_VERSION);
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)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
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;
}
JS_PUBLIC_API(bool)
JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks,
void* closure)
{
@@ -2323,120 +2416,106 @@ JS_StructuredClone(JSContext* cx, Handle
}
return buf.read(cx, vp, callbacks, closure);
}
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&
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;
}
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_;
-
- if (ownTransferables_ == OwnsTransferablesIfAny)
- DiscardTransferables(data_, nbytes_, callbacks, closure);
- ownTransferables_ = NoTransferables;
- js_free(data_);
- data_ = nullptr;
- nbytes_ = 0;
+ optionalCallbacks ? optionalCallbacks : data_.callbacks_;
+ void* closure = optionalClosure ? optionalClosure : data_.closure_;
+
+ 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_;
-
- data_ = nullptr;
- nbytes_ = 0;
+ *closure = data_.closure_;
+ *data = Move(data_);
+
version_ = 0;
- callbacks_ = 0;
- closure_ = 0;
- ownTransferables_ = NoTransferables;
+ data_.setOptionalCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
}
bool
JSAutoStructuredCloneBuffer::read(JSContext* cx, MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks,
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);
}
bool
JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
const JSStructuredCloneCallbacks* optionalCallbacks,
void* closure)
{
@@ -2446,28 +2525,26 @@ JSAutoStructuredCloneBuffer::write(JSCon
bool
JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
HandleValue transferable,
const JSStructuredCloneCallbacks* optionalCallbacks,
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;
}
JS_PUBLIC_API(bool)
JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1, uint32_t* p2)
{
return r->input().readPair((uint32_t*) p1, (uint32_t*) p2);
--- a/mfbt/BufferList.h
+++ b/mfbt/BufferList.h
@@ -73,16 +73,17 @@ class BufferList : private AllocPolicy
// an can be accessed via |Start()|. Subsequent buffers will be allocated with
// capacity aStandardCapacity.
BufferList(size_t aInitialSize,
size_t aInitialCapacity,
size_t aStandardCapacity,
AllocPolicy aAP = AllocPolicy())
: AllocPolicy(aAP),
mOwning(true),
+ mSegments(aAP),
mSize(0),
mStandardCapacity(aStandardCapacity)
{
MOZ_ASSERT(aInitialCapacity % kSegmentAlignment == 0);
MOZ_ASSERT(aStandardCapacity % kSegmentAlignment == 0);
if (aInitialCapacity) {
AllocateSegment(aInitialSize, aInitialCapacity);
@@ -254,17 +255,17 @@ class BufferList : private AllocPolicy
// Return a new BufferList that shares storage with this BufferList. The new
// BufferList is read-only. It allows iteration over aSize bytes starting at
// aIter. Borrow can fail, in which case *aSuccess will be false upon
// return. The borrowed BufferList can use a different AllocPolicy than the
// original one. However, it is not responsible for freeing buffers, so the
// 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
// will be false upon return. The new BufferList can use a different
// AllocPolicy than the original one. The new OtherAllocPolicy is responsible
// for freeing buffers, so the OtherAllocPolicy must use freeing method
// compatible to the original one.
template<typename OtherAllocPolicy>
@@ -427,17 +428,17 @@ BufferList<AllocPolicy>::FlattenBytes(It
}
return found;
}
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);
size_t size = aSize;
while (size) {
size_t toAdvance = std::min(size, aIter.RemainingInSegment());
if (!toAdvance || !result.mSegments.append(typename BufferList<BorrowingAllocPolicy>::Segment(aIter.mData, toAdvance, toAdvance))) {