Bug 1264642 - Part 4. Use BufferList to replace raw buffers in StructuredClone. r=baku,billm,jorendorff
☠☠ backed out by b25d09b7fab5 ☠ ☠
authorKan-Ru Chen <kanru@kanru.info>
Fri, 22 Apr 2016 18:04:20 +0800
changeset 309696 7c60fc4144fb682ae99c4068464fddf69791834f
parent 309695 9f434697ef2ecece5b93ad07093b85667d2018e3
child 309697 078e5c447f218e78162ae49eafedd925bd9b9275
push id30569
push userkwierso@gmail.com
push dateWed, 17 Aug 2016 23:34:23 +0000
treeherdermozilla-central@b25d09b7fab5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, billm, jorendorff
bugs1264642
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1264642 - Part 4. Use BufferList to replace raw buffers in StructuredClone. r=baku,billm,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
dom/base/StructuredCloneHolder.cpp
dom/base/StructuredCloneHolder.h
dom/base/nsFrameMessageManager.cpp
dom/base/nsStructuredCloneContainer.cpp
dom/broadcastchannel/BroadcastChannel.cpp
dom/broadcastchannel/BroadcastChannelChild.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/ipc/StructuredCloneData.cpp
dom/ipc/StructuredCloneData.h
dom/messagechannel/PMessagePort.ipdl
dom/messagechannel/SharedMessagePortMessage.cpp
dom/messagechannel/SharedMessagePortMessage.h
ipc/chromium/src/base/pickle.cc
ipc/chromium/src/base/pickle.h
ipc/glue/IPCMessageUtils.h
js/public/StructuredClone.h
js/src/builtin/TestingFunctions.cpp
js/src/vm/StructuredClone.cpp
mfbt/BufferList.h
--- 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()
 #ifdef DEBUG
   : mClearCalled(false)
 #endif
 {}
 
@@ -179,34 +179,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(&gCallbacks, this);
+  mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(&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,
@@ -314,84 +314,47 @@ 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(mSupportedContext == 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,
-                              aValue, &gCallbacks, this)) {
+  if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion,
+                              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(mSupportedContext == 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 {
@@ -95,30 +95,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;
 
 #ifdef DEBUG
   bool mClearCalled;
 #endif
 };
 
 class BlobImpl;
 class MessagePort;
@@ -170,22 +164,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()
@@ -264,39 +252,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;
   ContextSupport mSupportedContext;
 
   // Used for cloning blobs in the structured cloning algorithm.
   nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
 
   // This is used for sharing the backend of ImageBitmaps.
--- 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
@@ -153,19 +153,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
@@ -864,17 +864,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",
@@ -1035,17 +1035,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
@@ -1056,35 +1056,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,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 // static
@@ -1097,32 +1102,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,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 #if !defined(MOZ_B2G)
@@ -1136,36 +1143,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,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 #endif // MOZ_B2G
@@ -1287,25 +1297,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/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::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,19 @@ 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>();
+    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 +105,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 +140,19 @@ 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>();
+    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,
                             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
@@ -420,22 +420,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
@@ -104,17 +104,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;
 
   void EndRead(PickleIterator& iter) const;
--- 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"
 
@@ -118,19 +120,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.)
@@ -140,107 +142,137 @@ 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::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,
                         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) {
-    uint64_t* data_;
-    size_t nbytes_;
+    JSStructuredCloneData data_;
     uint32_t version_;
-    enum {
-        OwnsTransferablesIfAny,
-        IgnoreTransferablesIfAny,
-        NoTransferables
-    } ownTransferables_;
-
-    const JSStructuredCloneCallbacks* callbacks_;
-    void* closure_;
 
   public:
     JSAutoStructuredCloneBuffer()
-        : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
-          ownTransferables_(NoTransferables),
-          callbacks_(nullptr), closure_(nullptr)
-    {}
+        : version_(JS_STRUCTURED_CLONE_VERSION)
+    {
+        data_.setOptionalCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
+    }
 
     JSAutoStructuredCloneBuffer(const JSStructuredCloneCallbacks* callbacks, void* closure)
-        : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
-          ownTransferables_(NoTransferables),
-          callbacks_(callbacks), closure_(closure)
-    {}
+        : 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;
+            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());
+        const char* buffer;
+        size_t size = obj->data()->Size();
+        auto iter = obj->data()->Iter();
+        obj->data()->FlattenBytes(iter, &buffer, size);
+        JSString* str = JS_NewStringCopyN(cx, buffer, size);
         if (!str)
             return false;
         args.rval().setString(str);
         return true;
     }
 
     static bool
     getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
@@ -2244,21 +2247,21 @@ 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, &deserialized, nullptr, nullptr)) {
         return false;
     }
     args.rval().set(deserialized);
 
     if (hasTransferable)
         obj->discard();
 
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -146,16 +146,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);
@@ -163,49 +217,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);
+    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);
     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;
@@ -213,18 +271,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, const JSStructuredCloneCallbacks* cb,
                                      void* cbClosure)
@@ -291,18 +348,23 @@ struct JSStructuredCloneWriter {
         }
         return parseTransferable() && 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;
     }
 
   private:
     JSStructuredCloneWriter() = delete;
     JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
 
     JSContext* context() { return out.context(); }
 
@@ -405,77 +467,81 @@ 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,
                      const JSStructuredCloneCallbacks* cb, void* cbClosure,
                      Value transferable)
 {
     JSStructuredCloneWriter w(cx, 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, MutableHandleValue vp,
+ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, MutableHandleValue vp,
                     const JSStructuredCloneCallbacks* cb, void* cbClosure)
 {
-    SCInput in(cx, data, nbytes);
+    SCInput in(cx, data);
     JSStructuredCloneReader r(in, 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_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);
@@ -487,56 +553,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);
@@ -545,38 +616,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 {
@@ -586,45 +657,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);
 }
@@ -639,39 +717,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
@@ -692,53 +775,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;
     }
-    size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
-    size_t start = buf.length();
-    if (!buf.growByUninitialized(nwords))
-        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;
+    }
 
-    buf.back() = 0;  /* zero-pad to an 8-byte boundary */
+    // zero-pad to 8 bytes boundary
+    size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
+    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;
+    }
 
-    T* q = (T*) &buf[start];
-    copyAndSwapToLittleEndian(q, p, nelems);
     return true;
 }
 
 bool
 SCOutput::writeBytes(const void* p, size_t nbytes)
 {
     return writeArray((const uint8_t*) p, nbytes);
 }
@@ -761,37 +850,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
@@ -1301,33 +1405,33 @@ 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_TRANSFER_MAP_HEADER);
+    auto point = out.iter();
+    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;
 
@@ -1369,32 +1473,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;
@@ -1894,32 +1999,32 @@ JSStructuredCloneReader::startRead(Mutab
 
     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;
@@ -1965,31 +2070,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()));
@@ -2171,62 +2275,50 @@ 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, 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, vp, callbacks, closure);
+    return ReadStructuredClone(cx, buf, 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,
                         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, callbacks, closure, transferable);
+    return WriteStructuredClone(cx, value, bufp, 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)
 {
@@ -2261,119 +2353,105 @@ JS_StructuredClone(JSContext* cx, Handle
         }
     }
 
     return buf.read(cx, vp, callbacks, closure);
 }
 
 JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
 {
-    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);
     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_;
+        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_))
-        return false;
-
-    uint64_t* newData = static_cast<uint64_t*>(js_malloc(nbytes));
-    if (!newData)
+    if (StructuredCloneHasTransferObjects(srcData))
         return false;
 
-    js_memcpy(newData, srcData, nbytes);
+    clear();
 
-    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
 JSAutoStructuredCloneBuffer::read(JSContext* cx, MutableHandleValue vp,
                                   const JSStructuredCloneCallbacks* optionalCallbacks,
                                   void* closure)
 {
     MOZ_ASSERT(cx);
-    MOZ_ASSERT(data_);
-    return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp,
+    return !!JS_ReadStructuredClone(cx, data_, version_, vp,
                                     optionalCallbacks, closure);
 }
 
 bool
 JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
                                    const JSStructuredCloneCallbacks* optionalCallbacks,
                                    void* closure)
 {
@@ -2383,27 +2461,25 @@ 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_,
                                       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))) {