Bug 1264053 - MessagePort should support transferable objects in multi-e10s. r=sfink, r=smaug, r=jorendorff, r=janv, a=sledru
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 21 Jul 2016 15:29:42 +0200
changeset 349899 b6788af0c17645f95d93995904b63d7841f035ba
parent 349898 190417df2e2fec652cfabec2bb9a227f14c0de34
child 349900 cf237b1047044713b1b0db79a3b0af318a2090c3
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink, smaug, jorendorff, janv, sledru
bugs1264053
milestone50.0a2
Bug 1264053 - MessagePort should support transferable objects in multi-e10s. r=sfink, r=smaug, r=jorendorff, r=janv, a=sledru
dom/base/PostMessageEvent.cpp
dom/base/StructuredCloneHolder.cpp
dom/base/StructuredCloneHolder.h
dom/broadcastchannel/BroadcastChannel.cpp
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IndexedDatabaseInlines.h
dom/ipc/StructuredCloneData.h
dom/messagechannel/SharedMessagePortMessage.h
dom/workers/ServiceWorkerClient.cpp
dom/workers/WorkerPrivate.cpp
dom/xhr/XMLHttpRequestWorker.cpp
js/public/StructuredClone.h
js/src/builtin/TestingFunctions.cpp
js/src/jsapi-tests/testMappedArrayBuffer.cpp
js/src/vm/StructuredClone.cpp
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -31,17 +31,17 @@ namespace dom {
 
 PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
                                    const nsAString& aCallerOrigin,
                                    nsGlobalWindow* aTargetWindow,
                                    nsIPrincipal* aProvidedPrincipal,
                                    nsIDocument* aSourceDocument,
                                    bool aTrustedCaller)
 : StructuredCloneHolder(CloningSupported, TransferringSupported,
-                        SameProcessSameThread),
+                        StructuredCloneScope::SameProcessSameThread),
   mSource(aSource),
   mCallerOrigin(aCallerOrigin),
   mTargetWindow(aTargetWindow),
   mProvidedPrincipal(aProvidedPrincipal),
   mSourceDocument(aSourceDocument),
   mTrustedCaller(aTrustedCaller)
 {
   MOZ_COUNT_CTOR(PostMessageEvent);
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -141,19 +141,20 @@ const JSStructuredCloneCallbacks gCallba
   StructuredCloneCallbacksWriteTransfer,
   StructuredCloneCallbacksFreeTransfer
 };
 
 } // anonymous namespace
 
 // StructuredCloneHolderBase class
 
-StructuredCloneHolderBase::StructuredCloneHolderBase()
+StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope)
+  : mStructuredCloneScope(aScope)
 #ifdef DEBUG
-  : mClearCalled(false)
+  , mClearCalled(false)
 #endif
 {}
 
 StructuredCloneHolderBase::~StructuredCloneHolderBase()
 {
 #ifdef DEBUG
   MOZ_ASSERT(mClearCalled);
 #endif
@@ -179,17 +180,17 @@ 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 = new JSAutoStructuredCloneBuffer(mStructuredCloneScope, &gCallbacks, this);
 
   if (!mBuffer->write(aCx, aValue, aTransfer, &gCallbacks, this)) {
     mBuffer = nullptr;
     return false;
   }
 
   return true;
 }
@@ -237,20 +238,20 @@ StructuredCloneHolderBase::CustomFreeTra
 {
   MOZ_CRASH("Nothing to free.");
 }
 
 // StructuredCloneHolder class
 
 StructuredCloneHolder::StructuredCloneHolder(CloningSupport aSupportsCloning,
                                              TransferringSupport aSupportsTransferring,
-                                             ContextSupport aContext)
-  : mSupportsCloning(aSupportsCloning == CloningSupported)
+                                             StructuredCloneScope aScope)
+  : StructuredCloneHolderBase(aScope)
+  , mSupportsCloning(aSupportsCloning == CloningSupported)
   , mSupportsTransferring(aSupportsTransferring == TransferringSupported)
-  , mSupportedContext(aContext)
   , mParent(nullptr)
 #ifdef DEBUG
   , mCreationThread(NS_GetCurrentThread())
 #endif
 {}
 
 StructuredCloneHolder::~StructuredCloneHolder()
 {
@@ -267,41 +268,41 @@ StructuredCloneHolder::Write(JSContext* 
 }
 
 void
 StructuredCloneHolder::Write(JSContext* aCx,
                              JS::Handle<JS::Value> aValue,
                              JS::Handle<JS::Value> aTransfer,
                              ErrorResult& aRv)
 {
-  MOZ_ASSERT_IF(mSupportedContext == SameProcessSameThread,
+  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
 
   if (!StructuredCloneHolderBase::Write(aCx, aValue, aTransfer)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  if (mSupportedContext != SameProcessSameThread) {
+  if (mStructuredCloneScope != StructuredCloneScope::SameProcessSameThread) {
     for (uint32_t i = 0, len = mBlobImplArray.Length(); i < len; ++i) {
       if (!mBlobImplArray[i]->MayBeClonedToOtherThreads()) {
         aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
         return;
       }
     }
   }
 }
 
 void
 StructuredCloneHolder::Read(nsISupports* aParent,
                             JSContext* aCx,
                             JS::MutableHandle<JS::Value> aValue,
                             ErrorResult& aRv)
 {
-  MOZ_ASSERT_IF(mSupportedContext == SameProcessSameThread,
+  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
   MOZ_ASSERT(aParent);
 
   mozilla::AutoRestore<nsISupports*> guard(mParent);
   mParent = aParent;
 
   if (!StructuredCloneHolderBase::Read(aCx, aValue)) {
     JS_ClearPendingException(aCx);
@@ -332,37 +333,38 @@ void
 StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
                                       JSContext* aCx,
                                       uint64_t* aBuffer,
                                       size_t aBufferLength,
                                       uint32_t aAlgorithmVersion,
                                       JS::MutableHandle<JS::Value> aValue,
                                       ErrorResult& aRv)
 {
-  MOZ_ASSERT_IF(mSupportedContext == SameProcessSameThread,
+  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
 
   MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
   MOZ_ASSERT(aBuffer);
 
   mozilla::AutoRestore<nsISupports*> guard(mParent);
   mParent = aParent;
 
   if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength, aAlgorithmVersion,
-                              aValue, &gCallbacks, this)) {
+                              mStructuredCloneScope, aValue, &gCallbacks,
+                              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,
+  MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
 
   MOZ_ASSERT(mBuffer, "MoveBuffer() cannot be called without a Write().");
 
   if (NS_WARN_IF(!aArray.SetLength(BufferSize(), mozilla::fallible))) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
@@ -1036,18 +1038,18 @@ WriteFormData(JSStructuredCloneWriter* a
 
         closure->mHolder->BlobImpls().AppendElement(blobImpl);
         return true;
       }
 
       if (aValue.IsDirectory()) {
         Directory* directory = aValue.GetAsDirectory();
 
-        if (closure->mHolder->SupportedContext() !=
-              StructuredCloneHolder::SameProcessSameThread &&
+        if (closure->mHolder->CloneScope() !=
+              StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread &&
             !directory->ClonableToDifferentThreadOrProcess()) {
           return false;
         }
 
         return WriteDirectory(closure->mWriter, directory);
       }
 
       size_t charSize = sizeof(nsString::char_type);
@@ -1087,18 +1089,18 @@ StructuredCloneHolder::CustomReadHandler
     return ReadFileList(aCx, aReader, aIndex, this);
   }
 
   if (aTag == SCTAG_DOM_FORMDATA) {
     return ReadFormData(aCx, aReader, aIndex, this);
   }
 
   if (aTag == SCTAG_DOM_IMAGEBITMAP) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
 
     // Get the current global object.
     // This can be null.
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     // aIndex is the index of the cloned image.
     return ImageBitmap::ReadStructuredClone(aCx, aReader,
                                             parent, GetSurfaces(), aIndex);
    }
@@ -1122,17 +1124,17 @@ StructuredCloneHolder::CustomWriteHandle
       return WriteBlob(aWriter, blob, this);
     }
   }
 
   // See if this is a Directory object.
   {
     Directory* directory = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, aObj, directory))) {
-      if (mSupportedContext != SameProcessSameThread &&
+      if (mStructuredCloneScope != StructuredCloneScope::SameProcessSameThread &&
           !directory->ClonableToDifferentThreadOrProcess()) {
         return false;
       }
 
       return WriteDirectory(aWriter, directory);
     }
   }
 
@@ -1148,18 +1150,18 @@ StructuredCloneHolder::CustomWriteHandle
   {
     FormData* formData = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
       return WriteFormData(aWriter, formData, this);
     }
   }
 
   // See if this is an ImageBitmap object.
-  if (mSupportedContext == SameProcessSameThread ||
-      mSupportedContext == SameProcessDifferentThread) {
+  if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+      mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
     ImageBitmap* imageBitmap = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
       return ImageBitmap::WriteStructuredClone(aWriter,
                                                GetSurfaces(),
                                                imageBitmap);
     }
   }
 
@@ -1198,18 +1200,18 @@ StructuredCloneHolder::CustomReadTransfe
       return false;
     }
 
     aReturnObject.set(&value.toObject());
     return true;
   }
 
   if (aTag == SCTAG_DOM_CANVAS) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     OffscreenCanvasCloneData* data =
       static_cast<OffscreenCanvasCloneData*>(aContent);
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(parent, data);
     delete data;
 
     JS::Rooted<JS::Value> value(aCx);
@@ -1218,18 +1220,18 @@ StructuredCloneHolder::CustomReadTransfe
       return false;
     }
 
     aReturnObject.set(&value.toObject());
     return true;
   }
 
   if (aTag == SCTAG_DOM_IMAGEBITMAP) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     ImageBitmapCloneData* data =
       static_cast<ImageBitmapCloneData*>(aContent);
     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data);
     delete data;
 
     JS::Rooted<JS::Value> value(aCx);
@@ -1269,18 +1271,18 @@ StructuredCloneHolder::CustomWriteTransf
 
       *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
       *aOwnership = JS::SCTAG_TMO_CUSTOM;
       *aContent = nullptr;
 
       return true;
     }
 
-    if (mSupportedContext == SameProcessSameThread ||
-        mSupportedContext == SameProcessDifferentThread) {
+    if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
       OffscreenCanvas* canvas = nullptr;
       rv = UNWRAP_OBJECT(OffscreenCanvas, aObj, canvas);
       if (NS_SUCCEEDED(rv)) {
         MOZ_ASSERT(canvas);
 
         *aExtraData = 0;
         *aTag = SCTAG_DOM_CANVAS;
         *aOwnership = JS::SCTAG_TMO_CUSTOM;
@@ -1322,28 +1324,28 @@ StructuredCloneHolder::CustomFreeTransfe
   if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
     MOZ_ASSERT(!aContent);
     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
     MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
     return;
   }
 
   if (aTag == SCTAG_DOM_CANVAS) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     OffscreenCanvasCloneData* data =
       static_cast<OffscreenCanvasCloneData*>(aContent);
     delete data;
     return;
   }
 
   if (aTag == SCTAG_DOM_IMAGEBITMAP) {
-    MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
-               mSupportedContext == SameProcessDifferentThread);
+    MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
+               mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread);
     MOZ_ASSERT(aContent);
     ImageBitmapCloneData* data =
       static_cast<ImageBitmapCloneData*>(aContent);
     delete data;
     return;
   }
 }
 
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -26,17 +26,19 @@ namespace gfx {
 class DataSourceSurface;
 }
 
 namespace dom {
 
 class StructuredCloneHolderBase
 {
 public:
-  StructuredCloneHolderBase();
+  typedef JS::StructuredCloneScope StructuredCloneScope;
+
+  StructuredCloneHolderBase(StructuredCloneScope aScope = StructuredCloneScope::SameProcessSameThread);
   virtual ~StructuredCloneHolderBase();
 
   // These methods should be implemented in order to clone data.
   // Read more documentation in js/public/StructuredClone.h.
 
   virtual JSObject* CustomReadHandler(JSContext* aCx,
                                       JSStructuredCloneReader* aReader,
                                       uint32_t aTag,
@@ -110,16 +112,18 @@ public:
   {
     MOZ_ASSERT(mBuffer, "Write() has never been called.");
     return mBuffer->nbytes();
   }
 
 protected:
   nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
 
+  StructuredCloneScope mStructuredCloneScope;
+
 #ifdef DEBUG
   bool mClearCalled;
 #endif
 };
 
 class BlobImpl;
 class MessagePort;
 class MessagePortIdentifier;
@@ -134,34 +138,27 @@ public:
   };
 
   enum TransferringSupport
   {
     TransferringSupported,
     TransferringNotSupported
   };
 
-  enum ContextSupport
-  {
-    SameProcessSameThread,
-    SameProcessDifferentThread,
-    DifferentProcess
-  };
-
   // If cloning is supported, this object will clone objects such as Blobs,
   // FileList, ImageData, etc.
   // If transferring is supported, we will transfer MessagePorts and in the
   // future other transferrable objects.
-  // The ContextSupport is useful to know where the cloned/transferred data can
-  // be read and written. Additional checks about the nature of the objects
-  // will be done based on this context value because not all the objects can
-  // be sent between threads or processes.
+  // The StructuredCloneScope is useful to know where the cloned/transferred
+  // data can be read and written. Additional checks about the nature of the
+  // objects will be done based on this scope value because not all the
+  // objects can be sent between threads or processes.
   explicit StructuredCloneHolder(CloningSupport aSupportsCloning,
                                  TransferringSupport aSupportsTransferring,
-                                 ContextSupport aContextSupport);
+                                 StructuredCloneScope aStructuredCloneScope);
   virtual ~StructuredCloneHolder();
 
   // Normally you should just use Write() and Read().
 
   void Write(JSContext* aCx,
              JS::Handle<JS::Value> aValue,
              ErrorResult &aRv);
 
@@ -189,19 +186,19 @@ public:
   }
 
   nsTArray<RefPtr<BlobImpl>>& BlobImpls()
   {
     MOZ_ASSERT(mSupportsCloning, "Blobs cannot be taken/set if cloning is not supported.");
     return mBlobImplArray;
   }
 
-  ContextSupport SupportedContext() const
+  StructuredCloneScope CloneScope() const
   {
-    return mSupportedContext;
+    return mStructuredCloneScope;
   }
 
   // The parent object is set internally just during the Read(). This method
   // can be used by read functions to retrieve it.
   nsISupports* ParentDuringRead() const
   {
     return mParent;
   }
@@ -289,17 +286,16 @@ protected:
                       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.
   // The DataSourceSurface object must be thread-safely reference-counted.
   // The DataSourceSurface object will not be written ever via any ImageBitmap
   // instance, so no race condition will occur.
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -35,17 +35,17 @@ using namespace workers;
 
 class BroadcastChannelMessage final : public StructuredCloneHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(BroadcastChannelMessage)
 
   BroadcastChannelMessage()
     : StructuredCloneHolder(CloningSupported, TransferringNotSupported,
-                            DifferentProcess)
+                            StructuredCloneScope::DifferentProcess)
   {}
 
 private:
   ~BroadcastChannelMessage()
   {}
 };
 
 namespace {
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -143,21 +143,21 @@ class TransactionDatabaseOperationBase;
 class VersionChangeTransaction;
 
 /*******************************************************************************
  * Constants
  ******************************************************************************/
 
 // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
 // schema version.
-static_assert(JS_STRUCTURED_CLONE_VERSION == 6,
+static_assert(JS_STRUCTURED_CLONE_VERSION == 7,
               "Need to update the major schema version.");
 
 // Major schema version. Bump for almost everything.
-const uint32_t kMajorSchemaVersion = 23;
+const uint32_t kMajorSchemaVersion = 24;
 
 // Minor schema version. Should almost always be 0 (maybe bump on release
 // branches if we have to).
 const uint32_t kMinorSchemaVersion = 0;
 
 // The schema version we store in the SQLite database is a (signed) 32-bit
 // integer. The major version is left-shifted 4 bits so the max value is
 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
@@ -4096,16 +4096,29 @@ UpgradeSchemaFrom22_0To23_0(mozIStorageC
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
+UpgradeSchemaFrom23_0To24_0(mozIStorageConnection* aConnection)
+{
+  // The only change between 23 and 24 was a different structured clone format,
+  // but it's backwards-compatible.
+  nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(24, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
 GetDatabaseFileURL(nsIFile* aDatabaseFile,
                    PersistenceType aPersistenceType,
                    const nsACString& aGroup,
                    const nsACString& aOrigin,
                    uint32_t aTelemetryId,
                    nsIFileURL** aResult)
 {
   MOZ_ASSERT(aDatabaseFile);
@@ -4587,17 +4600,17 @@ CreateStorageConnection(nsIFile* aDBFile
       }
 
       rv = stmt->Execute();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else  {
       // This logic needs to change next time we change the schema!
-      static_assert(kSQLiteSchemaVersion == int32_t((23 << 4) + 0),
+      static_assert(kSQLiteSchemaVersion == int32_t((24 << 4) + 0),
                     "Upgrade function needed due to schema version increase.");
 
       while (schemaVersion != kSQLiteSchemaVersion) {
         if (schemaVersion == 4) {
           rv = UpgradeSchemaFrom4To5(connection);
         } else if (schemaVersion == 5) {
           rv = UpgradeSchemaFrom5To6(connection);
         } else if (schemaVersion == 6) {
@@ -4631,16 +4644,18 @@ CreateStorageConnection(nsIFile* aDBFile
         } else if (schemaVersion == MakeSchemaVersion(19, 0)) {
           rv = UpgradeSchemaFrom19_0To20_0(aFMDirectory, connection);
         } else if (schemaVersion == MakeSchemaVersion(20, 0)) {
           rv = UpgradeSchemaFrom20_0To21_0(connection);
         } else if (schemaVersion == MakeSchemaVersion(21, 0)) {
           rv = UpgradeSchemaFrom21_0To22_0(connection);
         } else if (schemaVersion == MakeSchemaVersion(22, 0)) {
           rv = UpgradeSchemaFrom22_0To23_0(connection, aOrigin);
+        } else if (schemaVersion == MakeSchemaVersion(23, 0)) {
+          rv = UpgradeSchemaFrom23_0To24_0(connection);
         } else {
           IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
                       "available!");
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
 
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -75,17 +75,19 @@ struct IDBObjectStore::StructuredCloneWr
   };
 
   JSAutoStructuredCloneBuffer mCloneBuffer;
   nsTArray<BlobOrMutableFile> mBlobOrMutableFiles;
   IDBDatabase* mDatabase;
   uint64_t mOffsetToKeyProp;
 
   explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase)
-    : mDatabase(aDatabase)
+    : mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
+                   nullptr)
+    , mDatabase(aDatabase)
     , mOffsetToKeyProp(0)
   {
     MOZ_ASSERT(aDatabase);
 
     MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
   }
 
   StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo)
@@ -1105,16 +1107,17 @@ IDBObjectStore::DeserializeValue(JSConte
     nullptr,
     nullptr,
     nullptr
   };
 
   // FIXME: Consider to use StructuredCloneHolder here and in other
   //        deserializing methods.
   if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
+                              JS::StructuredCloneScope::SameProcessSameThread,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 // static
@@ -1143,16 +1146,17 @@ IDBObjectStore::DeserializeIndexValue(JS
 
   static const JSStructuredCloneCallbacks callbacks = {
     CommonStructuredCloneReadCallback<IndexDeserializationHelper>,
     nullptr,
     nullptr
   };
 
   if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
+                              JS::StructuredCloneScope::SameProcessSameThread,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 #if !defined(MOZ_B2G)
@@ -1186,16 +1190,17 @@ IDBObjectStore::DeserializeUpgradeValue(
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr
   };
 
   if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
+                              JS::StructuredCloneScope::SameProcessSameThread,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
 #endif // MOZ_B2G
--- a/dom/indexedDB/IndexedDatabaseInlines.h
+++ b/dom/indexedDB/IndexedDatabaseInlines.h
@@ -42,25 +42,29 @@ StructuredCloneFile::operator==(const St
          this->mMutableFile == aOther.mMutableFile &&
          this->mFileInfo == aOther.mFileInfo &&
          this->mMutable == aOther.mMutable;
 }
 
 inline
 StructuredCloneReadInfo::StructuredCloneReadInfo()
   : mDatabase(nullptr)
+  , mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
+                 nullptr)
 {
   MOZ_COUNT_CTOR(StructuredCloneReadInfo);
 }
 
 inline
 StructuredCloneReadInfo::StructuredCloneReadInfo(
                              SerializedStructuredCloneReadInfo&& aCloneReadInfo)
   : mData(Move(aCloneReadInfo.data()))
   , mDatabase(nullptr)
+  , mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
+                 nullptr)
 {
   MOZ_COUNT_CTOR(StructuredCloneReadInfo);
 }
 
 inline
 StructuredCloneReadInfo::~StructuredCloneReadInfo()
 {
   MOZ_COUNT_DTOR(StructuredCloneReadInfo);
--- a/dom/ipc/StructuredCloneData.h
+++ b/dom/ipc/StructuredCloneData.h
@@ -75,17 +75,17 @@ private:
 };
 
 class StructuredCloneData : public StructuredCloneHolder
 {
 public:
   StructuredCloneData()
     : StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
                             StructuredCloneHolder::TransferringSupported,
-                            StructuredCloneHolder::DifferentProcess)
+                            StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
     , mExternalData(nullptr)
     , mExternalDataLength(0)
   {}
 
   StructuredCloneData(const StructuredCloneData&) = delete;
 
   ~StructuredCloneData()
   {
--- a/dom/messagechannel/SharedMessagePortMessage.h
+++ b/dom/messagechannel/SharedMessagePortMessage.h
@@ -19,17 +19,17 @@ class SharedMessagePortMessage final : p
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(SharedMessagePortMessage)
 
   nsTArray<uint8_t> mData;
 
   SharedMessagePortMessage()
     : StructuredCloneHolder(CloningSupported, TransferringSupported,
-                            DifferentProcess)
+                            StructuredCloneScope::DifferentProcess)
   {}
 
   void Read(nsISupports* aParent,
             JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue,
             ErrorResult& aRv);
 
   void Write(JSContext* aCx,
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -88,17 +88,17 @@ class ServiceWorkerClientPostMessageRunn
   : public Runnable
   , public StructuredCloneHolder
 {
   uint64_t mWindowId;
 
 public:
   explicit ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId)
     : StructuredCloneHolder(CloningSupported, TransferringSupported,
-                            SameProcessDifferentThread)
+                            StructuredCloneScope::SameProcessDifferentThread)
     , mWindowId(aWindowId)
   {}
 
   NS_IMETHOD
   Run()
   {
     AssertIsOnMainThread();
     nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -667,17 +667,17 @@ class MessageEventRunnable final : publi
 
   RefPtr<PromiseNativeHandler> mHandler;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior)
   : WorkerRunnable(aWorkerPrivate, aBehavior)
   , StructuredCloneHolder(CloningSupported, TransferringSupported,
-                          SameProcessDifferentThread)
+                          StructuredCloneScope::SameProcessDifferentThread)
   {
   }
 
   void
   SetServiceWorkerData(UniquePtr<ServiceWorkerClientInfo>&& aSource,
                        PromiseNativeHandler* aHandler)
   {
     mEventSource = Move(aSource);
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -239,17 +239,17 @@ class SendRunnable final
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   bool mHasUploadListeners;
 
 public:
   SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
                const nsAString& aStringBody)
   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   , StructuredCloneHolder(CloningSupported, TransferringNotSupported,
-                          SameProcessDifferentThread)
+                          StructuredCloneScope::SameProcessDifferentThread)
   , mStringBody(aStringBody)
   , mHasUploadListeners(false)
   {
   }
 
   void SetHaveUploadListeners(bool aHasUploadListeners)
   {
     mHasUploadListeners = aHasUploadListeners;
@@ -497,30 +497,30 @@ class EventRunnable final : public MainT
   JS::PersistentRooted<JSObject*> mScopeObj;
 
 public:
   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
                 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal,
                 JS::Handle<JSObject*> aScopeObj)
   : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
     StructuredCloneHolder(CloningSupported, TransferringNotSupported,
-                          SameProcessDifferentThread),
+                          StructuredCloneScope::SameProcessDifferentThread),
     mType(aType), mResponse(JS::UndefinedValue()), mLoaded(aLoaded),
     mTotal(aTotal), mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0),
     mReadyState(0), mUploadEvent(aUploadEvent), mProgressEvent(true),
     mLengthComputable(aLengthComputable), mUseCachedArrayBufferResponse(false),
     mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK),
     mScopeObj(GetJSRuntime(), aScopeObj)
   { }
 
   EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
                 JS::Handle<JSObject*> aScopeObj)
   : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
     StructuredCloneHolder(CloningSupported, TransferringNotSupported,
-                          SameProcessDifferentThread),
+                          StructuredCloneScope::SameProcessDifferentThread),
     mType(aType), mResponse(JS::UndefinedValue()), mLoaded(0), mTotal(0),
     mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
     mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
     mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
     mStatusResult(NS_OK), mResponseResult(NS_OK),
     mScopeObj(GetJSRuntime(), aScopeObj)
   { }
 
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -17,16 +17,23 @@
 
 struct JSRuntime;
 struct JSStructuredCloneReader;
 struct JSStructuredCloneWriter;
 
 // API for the HTML5 internal structured cloning algorithm.
 
 namespace JS {
+
+enum class StructuredCloneScope : uint32_t {
+    SameProcessSameThread,
+    SameProcessDifferentThread,
+    DifferentProcess
+};
+
 enum TransferableOwnership {
     /** Transferable data has not been filled in yet */
     SCTAG_TMO_UNFILLED = 0,
 
     /** Structured clone buffer does not yet own the data */
     SCTAG_TMO_UNOWNED = 1,
 
     /** All values at least this large are owned by the clone buffer */
@@ -129,39 +136,41 @@ typedef bool (*TransferStructuredCloneOp
  */
 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.)
-#define JS_STRUCTURED_CLONE_VERSION 6
+#define JS_STRUCTURED_CLONE_VERSION 7
 
 struct JSStructuredCloneCallbacks {
     ReadStructuredCloneOp read;
     WriteStructuredCloneOp write;
     StructuredCloneErrorOp reportError;
     ReadTransferStructuredCloneOp readTransfer;
     TransferStructuredCloneOp writeTransfer;
     FreeTransferStructuredCloneOp freeTransfer;
 };
 
 /** 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::StructuredCloneScope scope,
                        JS::MutableHandleValue vp,
                        const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
 
 /**
  * Note: On success, the caller is responsible for calling
  * JS_ClearStructuredClone(*datap, nbytes, optionalCallbacks, closure).
  */
 JS_PUBLIC_API(bool)
 JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, uint64_t** datap, size_t* nbytesp,
+                        JS::StructuredCloneScope scope,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void* closure, JS::HandleValue transferable);
 
 JS_PUBLIC_API(bool)
 JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void *closure, bool freeData = true);
 
@@ -169,37 +178,33 @@ JS_PUBLIC_API(bool)
 JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes, bool* hasTransferable);
 
 JS_PUBLIC_API(bool)
 JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
                    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
 
 /** RAII sugar for JS_WriteStructuredClone. */
 class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
+    const JS::StructuredCloneScope scope_;
     uint64_t* data_;
     size_t nbytes_;
     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)
-    {}
-
-    JSAutoStructuredCloneBuffer(const JSStructuredCloneCallbacks* callbacks, void* closure)
-        : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
+    JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
+                                const JSStructuredCloneCallbacks* callbacks, void* closure)
+        : scope_(scope), data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
           ownTransferables_(NoTransferables),
           callbacks_(callbacks), closure_(closure)
     {}
 
     JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
     JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other);
 
     ~JSAutoStructuredCloneBuffer() { clear(); }
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2206,17 +2206,17 @@ const JSPropertySpec CloneBufferObject::
     JS_PS_END
 };
 
 static bool
 Serialize(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    JSAutoStructuredCloneBuffer clonebuf;
+    JSAutoStructuredCloneBuffer clonebuf(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
     if (!clonebuf.write(cx, args.get(0), args.get(1)))
         return false;
 
     RootedObject obj(cx, CloneBufferObject::Create(cx, &clonebuf));
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
@@ -2248,17 +2248,20 @@ Deserialize(JSContext* cx, unsigned argc
     }
 
     bool hasTransferable;
     if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
         return false;
 
     RootedValue deserialized(cx);
     if (!JS_ReadStructuredClone(cx, obj->data(), obj->nbytes(),
-                                JS_STRUCTURED_CLONE_VERSION, &deserialized, nullptr, nullptr)) {
+                                JS_STRUCTURED_CLONE_VERSION,
+                                JS::StructuredCloneScope::SameProcessSameThread,
+                                &deserialized, nullptr, nullptr))
+    {
         return false;
     }
     args.rval().set(deserialized);
 
     if (hasTransferable)
         obj->discard();
 
     return true;
--- a/js/src/jsapi-tests/testMappedArrayBuffer.cpp
+++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp
@@ -121,17 +121,17 @@ bool TestDetachObject()
 
     return true;
 }
 
 bool TestCloneObject()
 {
     JS::RootedObject obj1(cx, CreateNewObject(8, 12));
     CHECK(obj1);
-    JSAutoStructuredCloneBuffer cloned_buffer;
+    JSAutoStructuredCloneBuffer cloned_buffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
     JS::RootedValue v1(cx, JS::ObjectValue(*obj1));
     CHECK(cloned_buffer.write(cx, v1, nullptr, nullptr));
     JS::RootedValue v2(cx);
     CHECK(cloned_buffer.read(cx, &v2, nullptr, nullptr));
     JS::RootedObject obj2(cx, v2.toObjectOrNull());
     CHECK(VerifyObject(obj2, 8, 12, false));
 
     return true;
@@ -159,17 +159,17 @@ bool TestTransferObject()
     JS::AutoValueVector argv(cx);
     if (!argv.append(v1))
         return false;
 
     JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::subarray(argv, 0, 1)));
     CHECK(obj);
     JS::RootedValue transferable(cx, JS::ObjectValue(*obj));
 
-    JSAutoStructuredCloneBuffer cloned_buffer;
+    JSAutoStructuredCloneBuffer cloned_buffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
     CHECK(cloned_buffer.write(cx, v1, transferable, nullptr, nullptr));
     JS::RootedValue v2(cx);
     CHECK(cloned_buffer.read(cx, &v2, nullptr, nullptr));
     JS::RootedObject obj2(cx, v2.toObjectOrNull());
     CHECK(VerifyObject(obj2, 8, 12, true));
     CHECK(JS_IsDetachedArrayBufferObject(obj1));
 
     return true;
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -66,16 +66,17 @@ using JS::CanonicalizeNaN;
 //
 // Note that SCTAG_END_OF_KEYS is written into the serialized form and should have
 // a stable ID, it need not be at the end of the list and should not be used for
 // sizing data structures.
 
 enum StructuredDataType : uint32_t {
     /* Structured data types provided by the engine */
     SCTAG_FLOAT_MAX = 0xFFF00000,
+    SCTAG_HEADER = 0xFFF10000,
     SCTAG_NULL = 0xFFFF0000,
     SCTAG_UNDEFINED,
     SCTAG_BOOLEAN,
     SCTAG_INT32,
     SCTAG_STRING,
     SCTAG_DATE_OBJECT,
     SCTAG_REGEXP_OBJECT,
     SCTAG_ARRAY_OBJECT,
@@ -221,27 +222,29 @@ class SCInput {
     uint64_t* point;
     uint64_t* bufEnd;
 };
 
 } /* namespace js */
 
 struct JSStructuredCloneReader {
   public:
-    explicit JSStructuredCloneReader(SCInput& in, const JSStructuredCloneCallbacks* cb,
+    explicit JSStructuredCloneReader(SCInput& in, JS::StructuredCloneScope scope,
+                                     const JSStructuredCloneCallbacks* cb,
                                      void* cbClosure)
-        : in(in), objs(in.context()), allObjs(in.context()),
+        : in(in), scope(scope), objs(in.context()), allObjs(in.context()),
           callbacks(cb), closure(cbClosure) { }
 
     SCInput& input() { return in; }
     bool read(MutableHandleValue vp);
 
   private:
     JSContext* context() { return in.context(); }
 
+    bool readHeader();
     bool readTransferMap();
 
     template <typename CharT>
     JSString* readStringImpl(uint32_t nchars);
     JSString* readString(uint32_t data);
 
     bool checkDouble(double d);
     bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
@@ -249,16 +252,18 @@ struct JSStructuredCloneReader {
     bool readDataView(uint32_t byteLength, MutableHandleValue vp);
     bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
     bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
     JSObject* readSavedFrame(uint32_t principalsTag);
     bool startRead(MutableHandleValue vp);
 
     SCInput& in;
 
+    JS::StructuredCloneScope scope;
+
     // Stack of objects with properties remaining to be read.
     AutoValueVector objs;
 
     // Stack of all objects read during this deserialization
     AutoValueVector allObjs;
 
     // The user defined callbacks that will be used for cloning.
     const JSStructuredCloneCallbacks* callbacks;
@@ -267,50 +272,52 @@ struct JSStructuredCloneReader {
     void* closure;
 
     friend bool JS_ReadTypedArray(JSStructuredCloneReader* r, MutableHandleValue vp);
 };
 
 struct JSStructuredCloneWriter {
   public:
     explicit JSStructuredCloneWriter(JSContext* cx,
+                                     JS::StructuredCloneScope scope,
                                      const JSStructuredCloneCallbacks* cb,
                                      void* cbClosure,
                                      Value tVal)
-        : out(cx), objs(out.context()),
+        : out(cx), scope(scope), objs(out.context()),
           counts(out.context()), entries(out.context()),
           memory(out.context()), callbacks(cb),
           closure(cbClosure), transferable(out.context(), tVal),
           transferableObjects(out.context(), GCHashSet<JSObject*>(cx))
     {}
 
     ~JSStructuredCloneWriter();
 
     bool init() {
         if (!memory.init()) {
             ReportOutOfMemory(context());
             return false;
         }
-        return parseTransferable() && writeTransferMap();
+        return parseTransferable() && writeHeader() && writeTransferMap();
     }
 
     bool write(HandleValue v);
 
     SCOutput& output() { return out; }
 
     bool extractBuffer(uint64_t** datap, size_t* sizep) {
         return out.extractBuffer(datap, sizep);
     }
 
   private:
     JSStructuredCloneWriter() = delete;
     JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
 
     JSContext* context() { return out.context(); }
 
+    bool writeHeader();
     bool writeTransferMap();
 
     bool writeString(uint32_t tag, JSString* str);
     bool writeArrayBuffer(HandleObject obj);
     bool writeTypedArray(HandleObject obj);
     bool writeDataView(HandleObject obj);
     bool writeSharedArrayBuffer(HandleObject obj);
     bool startObject(HandleObject obj, bool* backref);
@@ -324,16 +331,19 @@ struct JSStructuredCloneWriter {
 
     bool parseTransferable();
     bool transferOwnership();
 
     inline void checkStack();
 
     SCOutput out;
 
+    // The (address space, thread) scope within which this clone is valid.
+    JS::StructuredCloneScope scope;
+
     // Vector of objects with properties remaining to be written.
     //
     // NB: These can span multiple compartments, so the compartment must be
     // entered before any manipulation is performed.
     AutoValueVector objs;
 
     // counts[i] is the number of entries of objs[i] remaining to be written.
     // counts.length() == objs.length() and sum(counts) == entries.length().
@@ -406,29 +416,31 @@ ReportDataCloneError(JSContext* cx,
       default:
         MOZ_CRASH("Unkown errorId");
         break;
     }
 }
 
 bool
 WriteStructuredClone(JSContext* cx, HandleValue v, uint64_t** bufp, size_t* nbytesp,
+                     JS::StructuredCloneScope scope,
                      const JSStructuredCloneCallbacks* cb, void* cbClosure,
                      Value transferable)
 {
-    JSStructuredCloneWriter w(cx, cb, cbClosure, transferable);
+    JSStructuredCloneWriter w(cx, scope, cb, cbClosure, transferable);
     return w.init() && w.write(v) && w.extractBuffer(bufp, nbytesp);
 }
 
 bool
-ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes, MutableHandleValue vp,
+ReadStructuredClone(JSContext* cx, uint64_t* data, size_t nbytes,
+                    JS::StructuredCloneScope scope, MutableHandleValue vp,
                     const JSStructuredCloneCallbacks* cb, void* cbClosure)
 {
     SCInput in(cx, data, nbytes);
-    JSStructuredCloneReader r(in, cb, cbClosure);
+    JSStructuredCloneReader r(in, scope, cb, cbClosure);
     return r.read(vp);
 }
 
 // If the given buffer contains Transferables, free them. Note that custom
 // Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
 // delete their transferables.
 static void
 DiscardTransferables(uint64_t* buffer, size_t nbytes,
@@ -1259,16 +1271,22 @@ JSStructuredCloneWriter::startWrite(Hand
             return callbacks->write(context(), this, obj, closure);
         /* else fall through */
     }
 
     return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE);
 }
 
 bool
+JSStructuredCloneWriter::writeHeader()
+{
+    return out.writePair(SCTAG_HEADER, (uint32_t)scope);
+}
+
+bool
 JSStructuredCloneWriter::writeTransferMap()
 {
     if (transferableObjects.empty())
         return true;
 
     if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_UNREAD))
         return false;
 
@@ -1302,16 +1320,18 @@ JSStructuredCloneWriter::transferOwnersh
 {
     if (transferableObjects.empty())
         return true;
 
     // Walk along the transferables and the transfer map at the same time,
     // grabbing out pointers from the transferables and stuffing them into the
     // transfer map.
     uint64_t* point = out.rawBuffer();
+    MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_HEADER);
+    point++;
     MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
     point++;
     MOZ_ASSERT(LittleEndian::readUint64(point) == transferableObjects.count());
     point++;
 
     RootedObject obj(context());
     for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
         obj = tr.front();
@@ -1337,16 +1357,18 @@ JSStructuredCloneWriter::transferOwnersh
             Rooted<ArrayBufferObject*> arrayBuffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
             JSAutoCompartment ac(context(), arrayBuffer);
             size_t nbytes = arrayBuffer->byteLength();
 
             // Structured cloning currently only has optimizations for mapped
             // and malloc'd buffers, not asm.js-ified buffers.
             bool hasStealableContents = arrayBuffer->hasStealableContents() &&
                                         (arrayBuffer->isMapped() || arrayBuffer->hasMallocedContents());
+            if (scope == JS::StructuredCloneScope::DifferentProcess)
+                hasStealableContents = false;
 
             ArrayBufferObject::BufferContents bufContents =
                 ArrayBufferObject::stealContents(context(), arrayBuffer, hasStealableContents);
             if (!bufContents)
                 return false; // Destructor will clean up the already-transferred data.
 
             content = bufContents.data();
             tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
@@ -1892,16 +1914,39 @@ JSStructuredCloneReader::startRead(Mutab
 
     if (vp.isObject() && !allObjs.append(vp))
         return false;
 
     return true;
 }
 
 bool
+JSStructuredCloneReader::readHeader()
+{
+    uint32_t tag, data;
+    if (!in.getPair(&tag, &data))
+        return in.reportTruncated();
+
+    if (tag != SCTAG_HEADER) {
+        // Old structured clone buffer. We must have read it from disk or
+        // somewhere, so we can assume it's scope-compatible.
+        return true;
+    }
+
+    MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
+    if (data < uint32_t(scope)) {
+        JS_ReportErrorNumber(context(), GetErrorMessage, nullptr,
+                             JSMSG_SC_BAD_SERIALIZED_DATA, "incompatible structured clone scope");
+        return false;
+    }
+
+    return true;
+}
+
+bool
 JSStructuredCloneReader::readTransferMap()
 {
     JSContext* cx = context();
     uint64_t* headerPos = in.tell();
 
     uint32_t tag, data;
     if (!in.getPair(&tag, &data))
         return in.reportTruncated();
@@ -2066,16 +2111,19 @@ JSStructuredCloneReader::readSavedFrame(
 
     return savedFrame;
 }
 
 // Perform the whole recursive reading procedure.
 bool
 JSStructuredCloneReader::read(MutableHandleValue vp)
 {
+    if (!readHeader())
+        return false;
+
     if (!readTransferMap())
         return false;
 
     // Start out by reading in the main object and pushing it onto the 'objs'
     // stack. The data related to this object and its descendants extends from
     // here to the SCTAG_END_OF_KEYS at the end of the stream.
     if (!startRead(vp))
         return false;
@@ -2173,42 +2221,44 @@ JSStructuredCloneReader::read(MutableHan
 
     return true;
 }
 
 using namespace js;
 
 JS_PUBLIC_API(bool)
 JS_ReadStructuredClone(JSContext* cx, uint64_t* buf, size_t nbytes,
-                       uint32_t version, MutableHandleValue vp,
+                       uint32_t version, JS::StructuredCloneScope scope,
+                       MutableHandleValue vp,
                        const JSStructuredCloneCallbacks* optionalCallbacks,
                        void* closure)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     if (version > JS_STRUCTURED_CLONE_VERSION) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SC_BAD_CLONE_VERSION);
         return false;
     }
     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
-    return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure);
+    return ReadStructuredClone(cx, buf, nbytes, scope, vp, callbacks, closure);
 }
 
 JS_PUBLIC_API(bool)
 JS_WriteStructuredClone(JSContext* cx, HandleValue value, uint64_t** bufp, size_t* nbytesp,
+                        JS::StructuredCloneScope scope,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void* closure, HandleValue transferable)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
 
     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
-    return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable);
+    return WriteStructuredClone(cx, value, bufp, nbytesp, scope, callbacks, closure, transferable);
 }
 
 JS_PUBLIC_API(bool)
 JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void* closure, bool freeData)
 {
     DiscardTransferables(data, nbytes, optionalCallbacks, closure);
@@ -2242,17 +2292,17 @@ JS_StructuredClone(JSContext* cx, Handle
         return false;
       }
       vp.setString(strValue);
       return true;
     }
 
     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
 
-    JSAutoStructuredCloneBuffer buf;
+    JSAutoStructuredCloneBuffer buf(JS::StructuredCloneScope::SameProcessSameThread, callbacks, closure);
     {
         // If we use Maybe<AutoCompartment> here, G++ can't tell that the
         // destructor is only called when Maybe::construct was called, and
         // we get warnings about using uninitialized variables.
         if (value.isObject()) {
             AutoCompartment ac(cx, &value.toObject());
             if (!buf.write(cx, value, callbacks, closure))
                 return false;
@@ -2261,25 +2311,27 @@ JS_StructuredClone(JSContext* cx, Handle
                 return false;
         }
     }
 
     return buf.read(cx, vp, callbacks, closure);
 }
 
 JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
+    : scope_(other.scope_)
 {
     ownTransferables_ = other.ownTransferables_;
     other.steal(&data_, &nbytes_, &version_, &callbacks_, &closure_);
 }
 
 JSAutoStructuredCloneBuffer&
 JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
 {
     MOZ_ASSERT(&other != this);
+    MOZ_ASSERT(scope_ == other.scope_);
     clear();
     ownTransferables_ = other.ownTransferables_;
     other.steal(&data_, &nbytes_, &version_, &callbacks_, &closure_);
     return *this;
 }
 
 void
 JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCallbacks,
@@ -2364,17 +2416,17 @@ JSAutoStructuredCloneBuffer::steal(uint6
 
 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_, nbytes_, version_, scope_, vp,
                                     optionalCallbacks, closure);
 }
 
 bool
 JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
                                    const JSStructuredCloneCallbacks* optionalCallbacks,
                                    void* closure)
 {
@@ -2385,16 +2437,17 @@ 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_,
+                                      scope_,
                                       optionalCallbacks, closure,
                                       transferable);
 
     if (ok) {
         ownTransferables_ = OwnsTransferablesIfAny;
     } else {
         data_ = nullptr;
         nbytes_ = 0;