Bug 1264053 - MessagePort should support transferable objects in multi-e10s, r=sfink, r=smaug, r=jorendorff, r=janv
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 21 Jul 2016 15:29:42 +0200
changeset 309801 cf702debce85bb1aa7befb7839762a267fa70abe
parent 309800 ac65411ca5736e494fd394ff6565167d5ca3c9cf
child 309802 e3096cf1de9069442f0759f58228d423afb3b6e1
push id30570
push userkwierso@gmail.com
push dateWed, 17 Aug 2016 23:38:48 +0000
treeherdermozilla-central@a70835fe9f55 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink, smaug, jorendorff, janv
bugs1264053
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 1264053 - MessagePort should support transferable objects in multi-e10s, r=sfink, r=smaug, r=jorendorff, r=janv
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
@@ -144,21 +144,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.
@@ -4097,16 +4097,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);
@@ -4602,17 +4615,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) {
@@ -4646,16 +4659,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() override
   {
     AssertIsOnMainThread();
     nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -669,17 +669,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(RootingCx(), 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(RootingCx(), 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
@@ -2207,17 +2207,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);
@@ -2249,17 +2249,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;