Bug 1184557 - StructuredCloneHelper class for window to window postMessage, r=smaug
☠☠ backed out by 9be3d57c2e15 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 22 Jul 2015 19:37:18 +0100
changeset 285865 f6d29009ae0a599059d750ced11a8654a39fe224
parent 285864 74f5f13e1b63ed64273056e22ec88b9cb96d5017
child 285866 43e50f129af4ec22db4810366b8aa281c4aaed84
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs1184557
milestone42.0a1
Bug 1184557 - StructuredCloneHelper class for window to window postMessage, r=smaug
dom/base/PostMessageEvent.cpp
dom/base/PostMessageEvent.h
dom/base/StructuredCloneHelper.cpp
dom/base/StructuredCloneHelper.h
dom/base/nsGlobalWindow.cpp
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -18,256 +18,16 @@
 #include "nsGlobalWindow.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsPresContext.h"
 
 namespace mozilla {
 namespace dom {
 
-namespace {
-
-struct StructuredCloneInfo
-{
-  PostMessageEvent* event;
-  nsPIDOMWindow* window;
-
-  // This hashtable contains the transferred ports - used to avoid duplicates.
-  nsTArray<nsRefPtr<MessagePortBase>> transferredPorts;
-
-  // This array is populated when the ports are cloned.
-  nsTArray<nsRefPtr<MessagePortBase>> clonedPorts;
-};
-
-} // namespace
-
-const JSStructuredCloneCallbacks PostMessageEvent::sPostMessageCallbacks = {
-  PostMessageEvent::ReadStructuredClone,
-  PostMessageEvent::WriteStructuredClone,
-  nullptr,
-  PostMessageEvent::ReadTransferStructuredClone,
-  PostMessageEvent::TransferStructuredClone,
-  PostMessageEvent::FreeTransferStructuredClone
-};
-
-/* static */ JSObject*
-PostMessageEvent::ReadStructuredClone(JSContext* cx,
-                                      JSStructuredCloneReader* reader,
-                                      uint32_t tag,
-                                      uint32_t data,
-                                      void* closure)
-{
-  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
-  NS_ASSERTION(scInfo, "Must have scInfo!");
-
-  if (tag == SCTAG_DOM_BLOB) {
-    NS_ASSERTION(!data, "Data should be empty");
-
-    // What we get back from the reader is a BlobImpl.
-    // From that we create a new File.
-    BlobImpl* blobImpl;
-    if (JS_ReadBytes(reader, &blobImpl, sizeof(blobImpl))) {
-      MOZ_ASSERT(blobImpl);
-
-      // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
-      // called because the static analysis thinks dereferencing XPCOM objects
-      // can GC (because in some cases it can!), and a return statement with a
-      // JSObject* type means that JSObject* is on the stack as a raw pointer
-      // while destructors are running.
-      JS::Rooted<JS::Value> val(cx);
-      {
-        nsRefPtr<Blob> blob = Blob::Create(scInfo->window, blobImpl);
-        if (!ToJSValue(cx, blob, &val)) {
-          return nullptr;
-        }
-      }
-
-      return &val.toObject();
-    }
-  }
-
-  if (tag == SCTAG_DOM_FILELIST) {
-    NS_ASSERTION(!data, "Data should be empty");
-
-    // What we get back from the reader is a FileListClonedData.
-    // From that we create a new FileList.
-    FileListClonedData* fileListClonedData;
-    if (JS_ReadBytes(reader, &fileListClonedData, sizeof(fileListClonedData))) {
-      MOZ_ASSERT(fileListClonedData);
-
-      // nsRefPtr<FileList> needs to go out of scope before toObjectOrNull() is
-      // called because the static analysis thinks dereferencing XPCOM objects
-      // can GC (because in some cases it can!), and a return statement with a
-      // JSObject* type means that JSObject* is on the stack as a raw pointer
-      // while destructors are running.
-      JS::Rooted<JS::Value> val(cx);
-      {
-        nsRefPtr<FileList> fileList =
-          FileList::Create(scInfo->window, fileListClonedData);
-        if (!fileList || !ToJSValue(cx, fileList, &val)) {
-          return nullptr;
-        }
-      }
-
-      return &val.toObject();
-    }
-  }
-
-  const JSStructuredCloneCallbacks* runtimeCallbacks =
-    js::GetContextStructuredCloneCallbacks(cx);
-
-  if (runtimeCallbacks) {
-    return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
-  }
-
-  return nullptr;
-}
-
-/* static */ bool
-PostMessageEvent::WriteStructuredClone(JSContext* cx,
-                                       JSStructuredCloneWriter* writer,
-                                       JS::Handle<JSObject*> obj,
-                                       void *closure)
-{
-  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
-  NS_ASSERTION(scInfo, "Must have scInfo!");
-
-  // See if this is a File/Blob object.
-  {
-    Blob* blob = nullptr;
-    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
-      BlobImpl* blobImpl = blob->Impl();
-      if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
-          JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
-        scInfo->event->StoreISupports(blobImpl);
-        return true;
-      }
-    }
-  }
-
-  // See if this is a FileList object.
-  {
-    FileList* fileList = nullptr;
-    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, obj, fileList))) {
-      nsRefPtr<FileListClonedData> fileListClonedData =
-        fileList->CreateClonedData();
-      MOZ_ASSERT(fileListClonedData);
-      FileListClonedData* ptr = fileListClonedData.get();
-      if (JS_WriteUint32Pair(writer, SCTAG_DOM_FILELIST, 0) &&
-          JS_WriteBytes(writer, &ptr, sizeof(ptr))) {
-        scInfo->event->StoreISupports(fileListClonedData);
-        return true;
-      }
-    }
-  }
-
-  const JSStructuredCloneCallbacks* runtimeCallbacks =
-    js::GetContextStructuredCloneCallbacks(cx);
-
-  if (runtimeCallbacks) {
-    return runtimeCallbacks->write(cx, writer, obj, nullptr);
-  }
-
-  return false;
-}
-
-/* static */ bool
-PostMessageEvent::ReadTransferStructuredClone(JSContext* aCx,
-                                              JSStructuredCloneReader* reader,
-                                              uint32_t tag, void* aData,
-                                              uint64_t aExtraData,
-                                              void* aClosure,
-                                              JS::MutableHandle<JSObject*> returnObject)
-{
-  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
-  NS_ASSERTION(scInfo, "Must have scInfo!");
-
-  if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
-    MOZ_ASSERT(!aData);
-    // aExtraData is the index of this port identifier.
-    ErrorResult rv;
-    nsRefPtr<MessagePort> port =
-      MessagePort::Create(scInfo->window,
-                          scInfo->event->GetPortIdentifier(aExtraData),
-                          rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      return false;
-    }
-
-    scInfo->clonedPorts.AppendElement(port);
-
-    JS::Rooted<JS::Value> value(aCx);
-    if (!GetOrCreateDOMReflector(aCx, port, &value)) {
-      JS_ClearPendingException(aCx);
-      return false;
-    }
-
-    returnObject.set(&value.toObject());
-    return true;
-  }
-
-  return false;
-}
-
-/* static */ bool
-PostMessageEvent::TransferStructuredClone(JSContext* aCx,
-                                          JS::Handle<JSObject*> aObj,
-                                          void* aClosure,
-                                          uint32_t* aTag,
-                                          JS::TransferableOwnership* aOwnership,
-                                          void** aContent,
-                                          uint64_t* aExtraData)
-{
-  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
-  NS_ASSERTION(scInfo, "Must have scInfo!");
-
-  MessagePortBase* port = nullptr;
-  nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
-  if (NS_SUCCEEDED(rv)) {
-    if (scInfo->transferredPorts.Contains(port)) {
-      // No duplicates.
-      return false;
-    }
-
-    // We use aExtraData to store the index of this new port identifier.
-    MessagePortIdentifier* identifier =
-      scInfo->event->NewPortIdentifier(aExtraData);
-
-    if (!port->CloneAndDisentangle(*identifier)) {
-      return false;
-    }
-
-    scInfo->transferredPorts.AppendElement(port);
-
-    *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
-    *aOwnership = JS::SCTAG_TMO_CUSTOM;
-    *aContent = nullptr;
-
-    return true;
-  }
-
-  return false;
-}
-
-/* static */ void
-PostMessageEvent::FreeTransferStructuredClone(uint32_t aTag,
-                                              JS::TransferableOwnership aOwnership,
-                                              void *aContent,
-                                              uint64_t aExtraData,
-                                              void* aClosure)
-{
-  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
-    MOZ_ASSERT(aClosure);
-    MOZ_ASSERT(!aContent);
-
-    StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
-    MessagePort::ForceClose(scInfo->event->GetPortIdentifier(aExtraData));
-  }
-}
-
 PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
                                    const nsAString& aCallerOrigin,
                                    nsGlobalWindow* aTargetWindow,
                                    nsIPrincipal* aProvidedPrincipal,
                                    bool aTrustedCaller)
 : mSource(aSource),
   mCallerOrigin(aCallerOrigin),
   mTargetWindow(aTargetWindow),
@@ -277,30 +37,16 @@ PostMessageEvent::PostMessageEvent(nsGlo
   MOZ_COUNT_CTOR(PostMessageEvent);
 }
 
 PostMessageEvent::~PostMessageEvent()
 {
   MOZ_COUNT_DTOR(PostMessageEvent);
 }
 
-const MessagePortIdentifier&
-PostMessageEvent::GetPortIdentifier(uint64_t aId)
-{
-  MOZ_ASSERT(aId < mPortIdentifiers.Length());
-  return mPortIdentifiers[aId];
-}
-
-MessagePortIdentifier*
-PostMessageEvent::NewPortIdentifier(uint64_t* aPosition)
-{
-  *aPosition = mPortIdentifiers.Length();
-  return mPortIdentifiers.AppendElement();
-}
-
 NS_IMETHODIMP
 PostMessageEvent::Run()
 {
   MOZ_ASSERT(mTargetWindow->IsOuterWindow(),
              "should have been passed an outer window!");
   MOZ_ASSERT(!mSource || mSource->IsOuterWindow(),
              "should have been passed an outer window!");
 
@@ -342,38 +88,34 @@ PostMessageEvent::Run()
     //       don't do that in other places it seems better to hold the line for
     //       now.  Long-term, we want HTML5 to address this so that we can
     //       be compliant while being safer.
     if (!targetPrin->Equals(mProvidedPrincipal)) {
       return NS_OK;
     }
   }
 
-  // Deserialize the structured clone data
   JS::Rooted<JS::Value> messageData(cx);
-  StructuredCloneInfo scInfo;
-  scInfo.event = this;
-  scInfo.window = targetWindow;
-
-  if (!mBuffer.read(cx, &messageData, &sPostMessageCallbacks, &scInfo)) {
+  nsCOMPtr<nsPIDOMWindow> window = targetWindow.get();
+  if (!Read(window, cx, &messageData)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   // Create the event
   nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
     do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get()));
   nsRefPtr<MessageEvent> event =
     new MessageEvent(eventTarget, nullptr, nullptr);
 
   event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
                           false /*cancelable */, messageData, mCallerOrigin,
                           EmptyString(), mSource);
 
   event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
-                                      scInfo.clonedPorts));
+                                      GetTransferredPorts()));
 
   // We can't simply call dispatchEvent on the window because doing so ends
   // up flipping the trusted bit on the event, and we don't want that to
   // happen because then untrusted content can call postMessage on a chrome
   // window if it can get a reference to it.
 
   nsIPresShell *shell = targetWindow->GetExtantDoc()->GetShell();
   nsRefPtr<nsPresContext> presContext;
@@ -387,24 +129,10 @@ PostMessageEvent::Run()
   EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
                             presContext,
                             internalEvent,
                             static_cast<dom::Event*>(event.get()),
                             &status);
   return NS_OK;
 }
 
-bool
-PostMessageEvent::Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                        JS::Handle<JS::Value> aTransfer, nsPIDOMWindow* aWindow)
-{
-  // We *must* clone the data here, or the JS::Value could be modified
-  // by script
-  StructuredCloneInfo scInfo;
-  scInfo.event = this;
-  scInfo.window = aWindow;
-
-  return mBuffer.write(aCx, aMessage, aTransfer, &sPostMessageCallbacks,
-                       &scInfo);
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/PostMessageEvent.h
+++ b/dom/base/PostMessageEvent.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PostMessageEvent_h
 #define mozilla_dom_PostMessageEvent_h
 
-#include "js/StructuredClone.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 #include "nsCOMPtr.h"
 #include "nsRefPtr.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 
 class nsGlobalWindow;
 class nsIPrincipal;
 class nsPIDOMWindow;
@@ -23,87 +23,33 @@ namespace dom {
 class MessagePortBase;
 class MessagePortIdentifier;
 
 /**
  * Class used to represent events generated by calls to Window.postMessage,
  * which asynchronously creates and dispatches events.
  */
 class PostMessageEvent final : public nsRunnable
+                             , public StructuredCloneHelper
 {
 public:
   NS_DECL_NSIRUNNABLE
 
   PostMessageEvent(nsGlobalWindow* aSource,
                    const nsAString& aCallerOrigin,
                    nsGlobalWindow* aTargetWindow,
                    nsIPrincipal* aProvidedPrincipal,
                    bool aTrustedCaller);
 
-  bool Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-             JS::Handle<JS::Value> aTransfer, nsPIDOMWindow* aWindow);
-
 private:
   ~PostMessageEvent();
 
-  const MessagePortIdentifier& GetPortIdentifier(uint64_t aId);
-
-  MessagePortIdentifier* NewPortIdentifier(uint64_t* aPosition);
-
-  bool StoreISupports(nsISupports* aSupports)
-  {
-    mSupportsArray.AppendElement(aSupports);
-    return true;
-  }
-
-  static JSObject*
-  ReadStructuredClone(JSContext* cx,
-                      JSStructuredCloneReader* reader,
-                      uint32_t tag,
-                      uint32_t data,
-                      void* closure);
-
-  static bool
-  WriteStructuredClone(JSContext* cx,
-                       JSStructuredCloneWriter* writer,
-                       JS::Handle<JSObject*> obj,
-                       void *closure);
-
-  static bool
-  ReadTransferStructuredClone(JSContext* aCx,
-                              JSStructuredCloneReader* reader,
-                              uint32_t tag, void* aData,
-                              uint64_t aExtraData,
-                              void* aClosure,
-                              JS::MutableHandle<JSObject*> returnObject);
-
-  static bool
-  TransferStructuredClone(JSContext* aCx,
-                          JS::Handle<JSObject*> aObj,
-                          void* aClosure,
-                          uint32_t* aTag,
-                          JS::TransferableOwnership* aOwnership,
-                          void** aContent,
-                          uint64_t* aExtraData);
-
-  static void
-  FreeTransferStructuredClone(uint32_t aTag,
-                              JS::TransferableOwnership aOwnership,
-                              void *aContent,
-                              uint64_t aExtraData,
-                              void* aClosure);
-
-  static const JSStructuredCloneCallbacks sPostMessageCallbacks;
-
-  JSAutoStructuredCloneBuffer mBuffer;
   nsRefPtr<nsGlobalWindow> mSource;
   nsString mCallerOrigin;
   nsRefPtr<nsGlobalWindow> mTargetWindow;
   nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
   bool mTrustedCaller;
-  nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
-  nsTArray<MessagePortIdentifier> mPortIdentifiers;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PostMessageEvent_h
--- a/dom/base/StructuredCloneHelper.cpp
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -1,16 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "StructuredCloneHelper.h"
 
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/FileListBinding.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 JSObject*
 StructuredCloneCallbacksRead(JSContext* aCx,
                              JSStructuredCloneReader* aReader,
@@ -94,27 +98,40 @@ const JSStructuredCloneCallbacks gCallba
   StructuredCloneCallbacksError,
   StructuredCloneCallbacksReadTransfer,
   StructuredCloneCallbacksWriteTransfer,
   StructuredCloneCallbacksFreeTransfer
 };
 
 } // anonymous namespace
 
+// StructuredCloneHelperInternal class
+
 bool
 StructuredCloneHelperInternal::Write(JSContext* aCx,
                                      JS::Handle<JS::Value> aValue)
 {
   MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
 
   mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
   return mBuffer->write(aCx, aValue, &gCallbacks, this);
 }
 
 bool
+StructuredCloneHelperInternal::Write(JSContext* aCx,
+                                     JS::Handle<JS::Value> aValue,
+                                     JS::Handle<JS::Value> aTransfer)
+{
+  MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
+
+  mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
+  return mBuffer->write(aCx, aValue, aTransfer, &gCallbacks, this);
+}
+
+bool
 StructuredCloneHelperInternal::Read(JSContext* aCx,
                                     JS::MutableHandle<JS::Value> aValue)
 {
   MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
 
   bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this);
   mBuffer = nullptr;
   return ok;
@@ -127,17 +144,16 @@ StructuredCloneHelperInternal::ReadTrans
                                                     void* aContent,
                                                     uint64_t aExtraData,
                                                     JS::MutableHandleObject aReturnObject)
 {
   MOZ_CRASH("Nothing to read.");
   return false;
 }
 
-
 bool
 StructuredCloneHelperInternal::WriteTransferCallback(JSContext* aCx,
                                                      JS::Handle<JSObject*> aObj,
                                                      uint32_t* aTag,
                                                      JS::TransferableOwnership* aOwnership,
                                                      void** aContent,
                                                      uint64_t* aExtraData)
 {
@@ -149,10 +165,225 @@ void
 StructuredCloneHelperInternal::FreeTransferCallback(uint32_t aTag,
                                                     JS::TransferableOwnership aOwnership,
                                                     void* aContent,
                                                     uint64_t aExtraData)
 {
   MOZ_CRASH("Nothing to free.");
 }
 
+// StructuredCloneHelper class
+
+StructuredCloneHelper::StructuredCloneHelper(uint32_t aFlags)
+  : mFlags(aFlags)
+  , mParent(nullptr)
+{}
+
+StructuredCloneHelper::~StructuredCloneHelper()
+{}
+
+bool
+StructuredCloneHelper::Write(JSContext* aCx,
+                             JS::Handle<JS::Value> aValue,
+                             JS::Handle<JS::Value> aTransfer)
+{
+  bool ok = StructuredCloneHelperInternal::Write(aCx, aValue, aTransfer);
+  mTransferringPort.Clear();
+  return ok;
+}
+
+bool
+StructuredCloneHelper::Read(nsISupports* aParent,
+                            JSContext* aCx,
+                            JS::MutableHandle<JS::Value> aValue)
+{
+  mozilla::AutoRestore<nsISupports*> guard(mParent);
+  mParent = aParent;
+
+  return StructuredCloneHelperInternal::Read(aCx, aValue);
+}
+
+JSObject*
+StructuredCloneHelper::ReadCallback(JSContext* aCx,
+                                    JSStructuredCloneReader* aReader,
+                                    uint32_t aTag,
+                                    uint32_t aIndex)
+{
+  if (aTag == SCTAG_DOM_BLOB) {
+    MOZ_ASSERT(!(mFlags & eBlobNotSupported));
+
+    BlobImpl* blobImpl;
+    if (JS_ReadBytes(aReader, &blobImpl, sizeof(blobImpl))) {
+      MOZ_ASSERT(blobImpl);
+
+      // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
+      // called because the static analysis thinks dereferencing XPCOM objects
+      // can GC (because in some cases it can!), and a return statement with a
+      // JSObject* type means that JSObject* is on the stack as a raw pointer
+      // while destructors are running.
+      JS::Rooted<JS::Value> val(aCx);
+      {
+        nsRefPtr<Blob> blob = Blob::Create(mParent, blobImpl);
+        if (!ToJSValue(aCx, blob, &val)) {
+          return nullptr;
+        }
+      }
+
+      return &val.toObject();
+    }
+  }
+
+  if (aTag == SCTAG_DOM_FILELIST) {
+    MOZ_ASSERT(!(mFlags & eFileListNotSupported));
+
+    FileListClonedData* fileListClonedData;
+    if (JS_ReadBytes(aReader, &fileListClonedData,
+                     sizeof(fileListClonedData))) {
+      MOZ_ASSERT(fileListClonedData);
+
+      // nsRefPtr<FileList> needs to go out of scope before toObjectOrNull() is
+      // called because the static analysis thinks dereferencing XPCOM objects
+      // can GC (because in some cases it can!), and a return statement with a
+      // JSObject* type means that JSObject* is on the stack as a raw pointer
+      // while destructors are running.
+      JS::Rooted<JS::Value> val(aCx);
+      {
+        nsRefPtr<FileList> fileList =
+          FileList::Create(mParent, fileListClonedData);
+        if (!fileList || !ToJSValue(aCx, fileList, &val)) {
+          return nullptr;
+        }
+      }
+
+      return &val.toObject();
+    }
+  }
+
+  return NS_DOMReadStructuredClone(aCx, aReader, aTag, aIndex, nullptr);
+}
+
+bool
+StructuredCloneHelper::WriteCallback(JSContext* aCx,
+                                     JSStructuredCloneWriter* aWriter,
+                                     JS::Handle<JSObject*> aObj)
+{
+  // See if this is a File/Blob object.
+  if (!(mFlags & eBlobNotSupported)) {
+    Blob* blob = nullptr;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
+      BlobImpl* blobImpl = blob->Impl();
+      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, 0) &&
+             JS_WriteBytes(aWriter, &blobImpl, sizeof(blobImpl)) &&
+             StoreISupports(blobImpl);
+    }
+  }
+
+  if (!(mFlags & eFileListNotSupported)) {
+    FileList* fileList = nullptr;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
+      nsRefPtr<FileListClonedData> fileListClonedData =
+        fileList->CreateClonedData();
+      MOZ_ASSERT(fileListClonedData);
+      FileListClonedData* ptr = fileListClonedData.get();
+      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, 0) &&
+             JS_WriteBytes(aWriter, &ptr, sizeof(ptr)) &&
+             StoreISupports(fileListClonedData);
+    }
+  }
+
+  return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
+}
+
+bool
+StructuredCloneHelper::ReadTransferCallback(JSContext* aCx,
+                                            JSStructuredCloneReader* aReader,
+                                            uint32_t aTag,
+                                            void* aContent,
+                                            uint64_t aExtraData,
+                                            JS::MutableHandleObject aReturnObject)
+{
+  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+    MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
+
+    // This can be null.
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mParent);
+
+    MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
+    const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
+
+    // aExtraData is the index of this port identifier.
+    ErrorResult rv;
+    nsRefPtr<MessagePort> port =
+      MessagePort::Create(window, portIdentifier, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return false;
+    }
+
+    mTransferredPorts.AppendElement(port);
+
+    JS::Rooted<JS::Value> value(aCx);
+    if (!GetOrCreateDOMReflector(aCx, port, &value)) {
+      JS_ClearPendingException(aCx);
+      return false;
+    }
+
+    aReturnObject.set(&value.toObject());
+    return true;
+  }
+
+  return false;
+}
+
+
+bool
+StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
+                                             JS::Handle<JSObject*> aObj,
+                                             uint32_t* aTag,
+                                             JS::TransferableOwnership* aOwnership,
+                                             void** aContent,
+                                             uint64_t* aExtraData)
+{
+  if (!(mFlags & eMessagePortNotSupported)) {
+    MessagePortBase* port = nullptr;
+    nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
+    if (NS_SUCCEEDED(rv)) {
+      if (mTransferringPort.Contains(port)) {
+        // No duplicates.
+        return false;
+      }
+
+      // We use aExtraData to store the index of this new port identifier.
+      *aExtraData = mPortIdentifiers.Length();
+      MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement();
+
+      if (!port->CloneAndDisentangle(*identifier)) {
+        return false;
+      }
+
+      mTransferringPort.AppendElement(port);
+
+      *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
+      *aOwnership = JS::SCTAG_TMO_CUSTOM;
+      *aContent = nullptr;
+
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void
+StructuredCloneHelper::FreeTransferCallback(uint32_t aTag,
+                                            JS::TransferableOwnership aOwnership,
+                                            void* aContent,
+                                            uint64_t aExtraData)
+{
+  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+    MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
+    MOZ_ASSERT(!aContent);
+    MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
+    MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
+  }
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_StructuredCloneHelper_h
 #define mozilla_dom_StructuredCloneHelper_h
 
 #include "js/StructuredClone.h"
 #include "nsAutoPtr.h"
+#include "nsISupports.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 
 class StructuredCloneHelperInternal
 {
 public:
   // These methods should be implemented in order to clone data.
@@ -64,12 +66,115 @@ public:
 
   bool Read(JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue);
 
 protected:
   nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
 };
 
+class MessagePortBase;
+class MessagePortIdentifier;
+
+class StructuredCloneHelper : public StructuredCloneHelperInternal
+{
+public:
+  enum StructuredCloneHelperFlags {
+    eAll = 0,
+
+    // Disable the cloning of blobs. If a blob is part of the cloning value,
+    // an exception will be thrown.
+    eBlobNotSupported = 1 << 0,
+
+    // Disable the cloning of FileLists. If a FileList is part of the cloning
+    // value, an exception will be thrown.
+    eFileListNotSupported = 1 << 1,
+
+    // MessagePort can just be transfered. Using this flag we do not support
+    // the transfering.
+    eMessagePortNotSupported = 1 << 2,
+  };
+
+  // aFlags is a bitmap of StructuredCloneHelperFlags.
+  explicit StructuredCloneHelper(uint32_t aFlags = eAll);
+  virtual ~StructuredCloneHelper();
+
+  bool Write(JSContext* aCx,
+             JS::Handle<JS::Value> aValue,
+             JS::Handle<JS::Value> aTransfer);
+
+  bool Read(nsISupports* aParent,
+            JSContext* aCx,
+            JS::MutableHandle<JS::Value> aValue);
+
+  nsTArray<nsRefPtr<MessagePortBase>>& GetTransferredPorts()
+  {
+    MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
+    return mTransferredPorts;
+  }
+
+  // Custom Callbacks
+
+  virtual JSObject* ReadCallback(JSContext* aCx,
+                                 JSStructuredCloneReader* aReader,
+                                 uint32_t aTag,
+                                 uint32_t aIndex) override;
+
+  virtual bool WriteCallback(JSContext* aCx,
+                             JSStructuredCloneWriter* aWriter,
+                             JS::Handle<JSObject*> aObj) override;
+
+  virtual bool ReadTransferCallback(JSContext* aCx,
+                                    JSStructuredCloneReader* aReader,
+                                    uint32_t aTag,
+                                    void* aContent,
+                                    uint64_t aExtraData,
+                                    JS::MutableHandleObject aReturnObject) override;
+
+  virtual bool WriteTransferCallback(JSContext* aCx,
+                                     JS::Handle<JSObject*> aObj,
+                                     uint32_t* aTag,
+                                     JS::TransferableOwnership* aOwnership,
+                                     void** aContent,
+                                     uint64_t* aExtraData) override;
+
+  virtual void FreeTransferCallback(uint32_t aTag,
+                                    JS::TransferableOwnership aOwnership,
+                                    void* aContent,
+                                    uint64_t aExtraData) override;
+private:
+  bool StoreISupports(nsISupports* aSupports)
+  {
+    MOZ_ASSERT(aSupports);
+    mSupportsArray.AppendElement(aSupports);
+    return true;
+  }
+
+  // This is our bitmap.
+  uint32_t mFlags;
+
+  // Useful for the structured clone algorithm:
+
+  nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
+
+  // This raw pointer is set and unset into the ::Read(). It's always null
+  // outside that method. For this reason it's a raw pointer.
+  nsISupports* MOZ_NON_OWNING_REF mParent;
+
+  // This hashtable contains the ports while doing write (transferring and
+  // mapping transferred objects to the objects in the clone). It's an empty
+  // array outside the 'Write()' method.
+  nsTArray<nsRefPtr<MessagePortBase>> mTransferringPort;
+
+  // This array contains the ports once we've finished the reading. It's
+  // generated from the mPortIdentifiers array.
+  nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+
+  // This array contains the identifiers of the MessagePorts. Based on these we
+  // are able to reconnect the new transferred ports with the other
+  // MessageChannel ports.
+  nsTArray<MessagePortIdentifier> mPortIdentifiers;
+};
+
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_StructuredCloneHelper_h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8559,17 +8559,17 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
                          origin,
                          this,
                          providedPrincipal,
                          nsContentUtils::IsCallerChrome());
 
   JS::Rooted<JS::Value> message(aCx, aMessage);
   JS::Rooted<JS::Value> transfer(aCx, aTransfer);
 
-  if (!event->Write(aCx, message, transfer, this)) {
+  if (!event->Write(aCx, message, transfer)) {
     aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   aError = NS_DispatchToCurrentThread(event);
 }
 
 void