Bug 911972 - MessagePort and MessageChannel in workers, r=smaug, r=bent
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 17 Jun 2015 11:44:27 +0100
changeset 249353 964d75a56702a7b552e572ba30153ca6b2038fb4
parent 249352 54501a80ded17bfc4bff11b8c8be850de1595828
child 249354 27b0d2176ef1005380cc16a2c589f0cbe40d72ef
push id13617
push userryanvm@gmail.com
push dateWed, 17 Jun 2015 19:05:52 +0000
treeherderfx-team@6876a553b898 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, bent
bugs911972
milestone41.0a1
Bug 911972 - MessagePort and MessageChannel in workers, r=smaug, r=bent
dom/base/MessageChannel.cpp
dom/base/MessageChannel.h
dom/base/MessagePort.cpp
dom/base/MessagePort.h
dom/base/MessagePortList.cpp
dom/base/MessagePortList.h
dom/base/NodeInfo.cpp
dom/base/PostMessageEvent.cpp
dom/base/PostMessageEvent.h
dom/base/ProcessGlobal.cpp
dom/base/moz.build
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsCopySupport.cpp
dom/base/nsFrameMessageManager.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/test/chrome.ini
dom/base/test/iframe_messageChannel_chrome.html
dom/base/test/iframe_messageChannel_cloning.html
dom/base/test/iframe_messageChannel_pingpong.html
dom/base/test/iframe_messageChannel_post.html
dom/base/test/mochitest.ini
dom/base/test/test_messageChannel.html
dom/base/test/test_messageChannel.xul
dom/base/test/test_messageChannel_cloning.html
dom/base/test/test_messageChannel_pingpong.html
dom/base/test/test_messageChannel_post.html
dom/base/test/test_messageChannel_pref.html
dom/base/test/test_messageChannel_start.html
dom/base/test/test_messageChannel_transferable.html
dom/base/test/test_messageChannel_unshipped.html
dom/bindings/Bindings.conf
dom/broadcastchannel/BroadcastChannel.cpp
dom/broadcastchannel/BroadcastChannelChild.cpp
dom/ipc/StructuredCloneUtils.cpp
dom/ipc/StructuredCloneUtils.h
dom/messagechannel/MessageChannel.cpp
dom/messagechannel/MessageChannel.h
dom/messagechannel/MessagePort.cpp
dom/messagechannel/MessagePort.h
dom/messagechannel/MessagePortChild.cpp
dom/messagechannel/MessagePortChild.h
dom/messagechannel/MessagePortList.cpp
dom/messagechannel/MessagePortList.h
dom/messagechannel/MessagePortParent.cpp
dom/messagechannel/MessagePortParent.h
dom/messagechannel/MessagePortService.cpp
dom/messagechannel/MessagePortService.h
dom/messagechannel/MessagePortUtils.cpp
dom/messagechannel/MessagePortUtils.h
dom/messagechannel/PMessagePort.ipdl
dom/messagechannel/SharedMessagePortMessage.cpp
dom/messagechannel/SharedMessagePortMessage.h
dom/messagechannel/moz.build
dom/messagechannel/tests/chrome.ini
dom/messagechannel/tests/iframe_messageChannel_chrome.html
dom/messagechannel/tests/iframe_messageChannel_cloning.html
dom/messagechannel/tests/iframe_messageChannel_pingpong.html
dom/messagechannel/tests/iframe_messageChannel_post.html
dom/messagechannel/tests/iframe_messageChannel_sharedWorker2.html
dom/messagechannel/tests/iframe_messageChannel_transferable.html
dom/messagechannel/tests/mochitest.ini
dom/messagechannel/tests/moz.build
dom/messagechannel/tests/sharedWorker2_messageChannel.js
dom/messagechannel/tests/sharedWorker_messageChannel.js
dom/messagechannel/tests/test_messageChannel.html
dom/messagechannel/tests/test_messageChannel.xul
dom/messagechannel/tests/test_messageChannel_any.html
dom/messagechannel/tests/test_messageChannel_cloning.html
dom/messagechannel/tests/test_messageChannel_pingpong.html
dom/messagechannel/tests/test_messageChannel_post.html
dom/messagechannel/tests/test_messageChannel_pref.html
dom/messagechannel/tests/test_messageChannel_selfTransferring.html
dom/messagechannel/tests/test_messageChannel_sharedWorker.html
dom/messagechannel/tests/test_messageChannel_sharedWorker2.html
dom/messagechannel/tests/test_messageChannel_start.html
dom/messagechannel/tests/test_messageChannel_transferable.html
dom/messagechannel/tests/test_messageChannel_unshipped.html
dom/messagechannel/tests/test_messageChannel_worker.html
dom/messagechannel/tests/worker_messageChannel.js
dom/messagechannel/tests/worker_messageChannel_any.js
dom/moz.build
dom/webidl/MessageChannel.webidl
dom/workers/MessagePort.cpp
dom/workers/MessagePort.h
dom/workers/ServiceWorkerClient.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerStructuredClone.h
dom/workers/XMLHttpRequest.cpp
dom/workers/XMLHttpRequest.h
dom/workers/test/sharedWorker_sharedWorker.js
ipc/glue/BackgroundChild.h
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundImpl.cpp
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
js/public/StructuredClone.h
js/src/vm/StructuredClone.cpp
testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/event-ports-dedicated.html.ini
--- a/dom/base/NodeInfo.cpp
+++ b/dom/base/NodeInfo.cpp
@@ -106,17 +106,17 @@ NodeInfo::NodeInfo(nsIAtom *aName, nsIAt
 
 
 // nsISupports
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(NodeInfo)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(NodeInfo)
 
-static const char* kNSURIs[] = {
+static const char* kNodeInfoNSURIs[] = {
   " ([none])",
   " (xmlns)",
   " (xml)",
   " (xhtml)",
   " (XLink)",
   " (XSLT)",
   " (XBL)",
   " (MathML)",
@@ -124,18 +124,18 @@ static const char* kNSURIs[] = {
   " (XUL)"
 };
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(NodeInfo)
   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
     char name[72];
     uint32_t nsid = tmp->NamespaceID();
     nsAtomCString localName(tmp->NameAtom());
-    if (nsid < ArrayLength(kNSURIs)) {
-      PR_snprintf(name, sizeof(name), "NodeInfo%s %s", kNSURIs[nsid],
+    if (nsid < ArrayLength(kNodeInfoNSURIs)) {
+      PR_snprintf(name, sizeof(name), "NodeInfo%s %s", kNodeInfoNSURIs[nsid],
                   localName.get());
     }
     else {
       PR_snprintf(name, sizeof(name), "NodeInfo %s", localName.get());
     }
 
     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   }
new file mode 100644
--- /dev/null
+++ b/dom/base/PostMessageEvent.cpp
@@ -0,0 +1,390 @@
+/* -*- 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 "PostMessageEvent.h"
+
+#include "MessageEvent.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/PMessagePort.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/EventDispatcher.h"
+#include "nsGlobalWindow.h"
+#include "nsIPresShell.h"
+#include "nsIPrincipal.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+struct StructuredCloneInfo
+{
+  PostMessageEvent* event;
+  bool subsumes;
+  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;
+};
+
+} // anonymous 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");
+
+    nsISupports* supports;
+    if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
+      JS::Rooted<JS::Value> val(cx);
+      if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
+        return val.toObjectOrNull();
+      }
+    }
+  }
+
+  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 (scInfo->subsumes && 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;
+      }
+    }
+  }
+
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+  nsContentUtils::XPConnect()->
+    GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
+  if (wrappedNative) {
+    uint32_t scTag = 0;
+    nsISupports* supports = wrappedNative->Native();
+
+    nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
+    if (list && scInfo->subsumes)
+      scTag = SCTAG_DOM_FILELIST;
+
+    if (scTag)
+      return JS_WriteUint32Pair(writer, scTag, 0) &&
+             JS_WriteBytes(writer, &supports, sizeof(supports)) &&
+             scInfo->event->StoreISupports(supports);
+  }
+
+  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)
+{
+  // Nothing to do.
+}
+
+PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
+                                   const nsAString& aCallerOrigin,
+                                   nsGlobalWindow* aTargetWindow,
+                                   nsIPrincipal* aProvidedPrincipal,
+                                   bool aTrustedCaller)
+: mSource(aSource),
+  mCallerOrigin(aCallerOrigin),
+  mTargetWindow(aTargetWindow),
+  mProvidedPrincipal(aProvidedPrincipal),
+  mTrustedCaller(aTrustedCaller)
+{
+  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!");
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  JSContext* cx = jsapi.cx();
+
+  // If we bailed before this point we're going to leak mMessage, but
+  // that's probably better than crashing.
+
+  nsRefPtr<nsGlobalWindow> targetWindow;
+  if (mTargetWindow->IsClosedOrClosing() ||
+      !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
+      targetWindow->IsClosedOrClosing())
+    return NS_OK;
+
+  MOZ_ASSERT(targetWindow->IsInnerWindow(),
+             "we ordered an inner window!");
+  JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
+
+  // Ensure that any origin which might have been provided is the origin of this
+  // window's document.  Note that we do this *now* instead of when postMessage
+  // is called because the target window might have been navigated to a
+  // different location between then and now.  If this check happened when
+  // postMessage was called, it would be fairly easy for a malicious webpage to
+  // intercept messages intended for another site by carefully timing navigation
+  // of the target window so it changed location after postMessage but before
+  // now.
+  if (mProvidedPrincipal) {
+    // Get the target's origin either from its principal or, in the case the
+    // principal doesn't carry a URI (e.g. the system principal), the target's
+    // document.
+    nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
+    if (NS_WARN_IF(!targetPrin))
+      return NS_OK;
+
+    // Note: This is contrary to the spec with respect to file: URLs, which
+    //       the spec groups into a single origin, but given we intentionally
+    //       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)) {
+    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));
+
+  // 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;
+  if (shell)
+    presContext = shell->GetPresContext();
+
+  event->SetTrusted(mTrustedCaller);
+  WidgetEvent* internalEvent = event->GetInternalNSEvent();
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+  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, bool aSubsumes,
+                        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;
+  scInfo.subsumes = aSubsumes;
+
+  return mBuffer.write(aCx, aMessage, aTransfer, &sPostMessageCallbacks,
+                       &scInfo);
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/base/PostMessageEvent.h
@@ -0,0 +1,108 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_PostMessageEvent_h
+#define mozilla_dom_PostMessageEvent_h
+
+#include "js/StructuredClone.h"
+#include "nsCOMPtr.h"
+#include "nsRefPtr.h"
+#include "nsTArray.h"
+
+class nsGlobalWindow;
+class nsIPrincipal;
+
+namespace mozilla {
+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:
+  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, bool aSubsumes,
+             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;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_PostMessageEvent_h
--- a/dom/base/ProcessGlobal.cpp
+++ b/dom/base/ProcessGlobal.cpp
@@ -2,16 +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/. */
 
 #include "ProcessGlobal.h"
 
 #include "nsContentCID.h"
+#include "nsDOMClassInfoID.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 ProcessGlobal::ProcessGlobal(nsFrameMessageManager* aMessageManager)
  : mInitialized(false),
    mMessageManager(aMessageManager)
 {
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -174,19 +174,16 @@ EXPORTS.mozilla.dom += [
     'ElementInlines.h',
     'EventSource.h',
     'File.h',
     'FragmentOrElement.h',
     'FromParser.h',
     'ImageEncoder.h',
     'ImportManager.h',
     'Link.h',
-    'MessageChannel.h',
-    'MessagePort.h',
-    'MessagePortList.h',
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'PerformanceEntry.h',
     'PerformanceMark.h',
     'PerformanceMeasure.h',
@@ -232,18 +229,16 @@ UNIFIED_SOURCES += [
     'Element.cpp',
     'EventSource.cpp',
     'File.cpp',
     'FileIOObject.cpp',
     'FragmentOrElement.cpp',
     'ImageEncoder.cpp',
     'ImportManager.cpp',
     'Link.cpp',
-    'MessageChannel.cpp',
-    'MessagePortList.cpp',
     'MultipartBlobImpl.cpp',
     'Navigator.cpp',
     'NodeInfo.cpp',
     'NodeIterator.cpp',
     'nsAtomListUtils.cpp',
     'nsAttrAndChildArray.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
@@ -324,16 +319,17 @@ UNIFIED_SOURCES += [
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'nsXMLHttpRequest.cpp',
     'nsXMLNameSpaceMap.cpp',
     'PerformanceEntry.cpp',
     'PerformanceMark.cpp',
     'PerformanceMeasure.cpp',
     'PerformanceResourceTiming.cpp',
+    'PostMessageEvent.cpp',
     'ProcessGlobal.cpp',
     'ResponsiveImageSelector.cpp',
     'SameProcessMessageQueue.cpp',
     'ScriptSettings.cpp',
     'ShadowRoot.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'Text.cpp',
@@ -348,18 +344,16 @@ UNIFIED_SOURCES += [
 
 if CONFIG['MOZ_WEBRTC']:
     UNIFIED_SOURCES += [
         'nsDOMDataChannel.cpp',
     ]
 
 # these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
 SOURCES += [
-    # this file doesn't like windows.h
-    'MessagePort.cpp',
     # Because of OS X headers.
     'nsContentUtils.cpp',
     # this file doesn't like windows.h
     'nsDOMWindowUtils.cpp',
     # Conflicts with windows.h's definition of SendMessage.
     'nsFrameMessageManager.cpp',
     # This file has a #error "Never include windows.h in this file!"
     'nsGlobalWindow.cpp',
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -213,16 +213,17 @@ const char kLoadAsData[] = "loadAsData";
 
 nsIXPConnect *nsContentUtils::sXPConnect;
 nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
 nsIPrincipal *nsContentUtils::sSystemPrincipal;
 nsIPrincipal *nsContentUtils::sNullSubjectPrincipal;
 nsIParserService *nsContentUtils::sParserService = nullptr;
 nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
 nsIIOService *nsContentUtils::sIOService;
+nsIUUIDGenerator *nsContentUtils::sUUIDGenerator;
 nsIConsoleService *nsContentUtils::sConsoleService;
 nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
 nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nullptr;
 nsIStringBundleService *nsContentUtils::sStringBundleService;
 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
 nsIContentPolicy *nsContentUtils::sContentPolicyService;
 bool nsContentUtils::sTriedToGetContentPolicy = false;
@@ -546,16 +547,23 @@ nsContentUtils::Init()
 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   Preferences::AddBoolVarCache(&sDOMWindowDumpEnabled,
                                "browser.dom.window.dump.enabled");
 #endif
 
   Element::InitCCCallbacks();
 
+  nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
+    do_GetService("@mozilla.org/uuid-generator;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  uuidGenerator.forget(&sUUIDGenerator);
+
   sInitialized = true;
 
   return NS_OK;
 }
 
 void
 nsContentUtils::GetShiftText(nsAString& text)
 {
@@ -1798,16 +1806,17 @@ nsContentUtils::Shutdown()
   NS_IF_RELEASE(sStringBundleService);
   NS_IF_RELEASE(sConsoleService);
   sXPConnect = nullptr;
   NS_IF_RELEASE(sSecurityManager);
   NS_IF_RELEASE(sSystemPrincipal);
   NS_IF_RELEASE(sNullSubjectPrincipal);
   NS_IF_RELEASE(sParserService);
   NS_IF_RELEASE(sIOService);
+  NS_IF_RELEASE(sUUIDGenerator);
   NS_IF_RELEASE(sLineBreaker);
   NS_IF_RELEASE(sWordBreaker);
   NS_IF_RELEASE(sBidiKeyboard);
 
   delete sAtomEventTable;
   sAtomEventTable = nullptr;
   delete sStringEventTable;
   sStringEventTable = nullptr;
@@ -7145,16 +7154,29 @@ nsContentUtils::IsJavascriptMIMEType(con
     if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
       return true;
     }
   }
 
   return false;
 }
 
+nsresult
+nsContentUtils::GenerateUUIDInPlace(nsID& aUUID)
+{
+  MOZ_ASSERT(sUUIDGenerator);
+
+  nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&aUUID);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 uint64_t
 nsContentUtils::GetInnerWindowID(nsIRequest* aRequest)
 {
   // can't do anything if there's no nsIRequest!
   if (!aRequest) {
     return 0;
   }
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -80,16 +80,17 @@ class nsIRequest;
 class nsIRunnable;
 class nsIScriptContext;
 class nsIScriptSecurityManager;
 class nsIStringBundle;
 class nsIStringBundleService;
 class nsISupportsArray;
 class nsISupportsHashKey;
 class nsIURI;
+class nsIUUIDGenerator;
 class nsIWidget;
 class nsIWordBreaker;
 class nsIXPConnect;
 class nsNodeInfoManager;
 class nsPIDOMWindow;
 class nsPresContext;
 class nsStringBuffer;
 class nsStringHashKey;
@@ -848,16 +849,21 @@ public:
    * A helper function that parses a sandbox attribute (of an <iframe> or
    * a CSP directive) and converts it to the set of flags used internally.
    *
    * @param sandboxAttr   the sandbox attribute
    * @return              the set of flags (0 if sandboxAttr is null)
    */
   static uint32_t ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr);
 
+  /**
+   * Helper function that generates a UUID.
+   */
+  static nsresult GenerateUUIDInPlace(nsID& aUUID);
+
 
   /**
    * Fill (with the parameters given) the localized string named |aKey| in
    * properties file |aFile|.
    */
 private:
   static nsresult FormatLocalizedString(PropertiesFile aFile,
                                         const char* aKey,
@@ -2438,16 +2444,17 @@ private:
   static nsIPrincipal *sSystemPrincipal;
   static nsIPrincipal *sNullSubjectPrincipal;
 
   static nsIParserService *sParserService;
 
   static nsNameSpaceManager *sNameSpaceManager;
 
   static nsIIOService *sIOService;
+  static nsIUUIDGenerator *sUUIDGenerator;
 
   static bool sImgLoaderInitialized;
   static void InitImgLoader();
 
   // The following four members are initialized lazily
   static imgLoader* sImgLoader;
   static imgLoader* sPrivateImgLoader;
   static imgICache* sImgCache;
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -6,16 +6,17 @@
 
 #include "nsCopySupport.h"
 #include "nsIDocumentEncoder.h"
 #include "nsISupports.h"
 #include "nsIContent.h"
 #include "nsIComponentManager.h" 
 #include "nsIServiceManager.h"
 #include "nsIClipboard.h"
+#include "nsIFormControl.h"
 #include "nsISelection.h"
 #include "nsWidgetsCID.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIDOMRange.h"
 #include "nsRange.h"
 #include "imgIContainer.h"
 #include "nsIPresShell.h"
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -272,25 +272,25 @@ template<ActorFlavorEnum Flavor>
 static bool
 BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType* aManager,
                        const StructuredCloneData& aData,
                        ClonedMessageData& aClonedData)
 {
   SerializedStructuredCloneBuffer& buffer = aClonedData.data();
   buffer.data = aData.mData;
   buffer.dataLength = aData.mDataLength;
-  const nsTArray<nsRefPtr<Blob>>& blobs = aData.mClosure.mBlobs;
-  if (!blobs.IsEmpty()) {
+  const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = aData.mClosure.mBlobImpls;
+  if (!blobImpls.IsEmpty()) {
     typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
     InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData);
-    uint32_t length = blobs.Length();
+    uint32_t length = blobImpls.Length();
     blobList.SetCapacity(length);
     for (uint32_t i = 0; i < length; ++i) {
       typename BlobTraits<Flavor>::BlobType* protocolActor =
-        aManager->GetOrCreateActorForBlob(blobs[i]);
+        aManager->GetOrCreateActorForBlobImpl(blobImpls[i]);
       if (!protocolActor) {
         return false;
       }
       blobList.AppendElement(protocolActor);
     }
   }
   return true;
 }
@@ -318,29 +318,26 @@ UnpackClonedMessageData(const ClonedMess
   const SerializedStructuredCloneBuffer& buffer = aData.data();
   typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
   const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aData);
   StructuredCloneData cloneData;
   cloneData.mData = buffer.data;
   cloneData.mDataLength = buffer.dataLength;
   if (!blobs.IsEmpty()) {
     uint32_t length = blobs.Length();
-    cloneData.mClosure.mBlobs.SetCapacity(length);
+    cloneData.mClosure.mBlobImpls.SetCapacity(length);
     for (uint32_t i = 0; i < length; ++i) {
       auto* blob =
         static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]);
       MOZ_ASSERT(blob);
 
       nsRefPtr<BlobImpl> blobImpl = blob->GetBlobImpl();
       MOZ_ASSERT(blobImpl);
 
-      // This object will be duplicated with a correct parent before being
-      // exposed to JS.
-      nsRefPtr<Blob> domBlob = Blob::Create(nullptr, blobImpl);
-      cloneData.mClosure.mBlobs.AppendElement(domBlob);
+      cloneData.mClosure.mBlobImpls.AppendElement(blobImpl);
     }
   }
   return cloneData;
 }
 
 StructuredCloneData
 mozilla::dom::ipc::UnpackClonedMessageDataForParent(const ClonedMessageData& aData)
 {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -54,29 +54,28 @@
 #include "mozilla/unused.h"
 
 // Other Classes
 #include "mozilla/dom/BarProps.h"
 #include "nsContentCID.h"
 #include "nsLayoutStatics.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozilla/dom/workers/Workers.h"
-#include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "nsJSPrincipals.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Debug.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "AudioChannelService.h"
-#include "MessageEvent.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
+#include "PostMessageEvent.h"
 
 // Interfaces Needed
 #include "nsIFrame.h"
 #include "nsCanvasFrame.h"
 #include "nsIWidget.h"
 #include "nsIWidgetListener.h"
 #include "nsIBaseWindow.h"
 #include "nsIDeviceSensors.h"
@@ -174,23 +173,19 @@
 #include "nsFrameLoader.h"
 #include "nsISupportsPrimitives.h"
 #include "nsXPCOMCID.h"
 #include "mozilla/Logging.h"
 #include "prenv.h"
 #include "prprf.h"
 
 #include "mozilla/dom/MessageChannel.h"
-#include "mozilla/dom/MessagePort.h"
-#include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/Promise.h"
 
-#include "mozilla/dom/StructuredCloneTags.h"
-
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/Gamepad.h"
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #include "mozilla/dom/VRDevice.h"
 
 #include "nsRefreshDriver.h"
@@ -202,17 +197,16 @@
 #include "nsHTMLDocument.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "prrng.h"
 #include "nsSandboxFlags.h"
 #include "TimeChangeObserver.h"
 #include "TouchCaret.h"
 #include "mozilla/dom/AudioContext.h"
-#include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/HashChangeEvent.h"
 #include "mozilla/dom/MozSelfSupportBinding.h"
 #include "mozilla/dom/PopStateEvent.h"
@@ -8033,377 +8027,16 @@ nsGlobalWindow::CallerInnerWindow()
   }
 
   // The calling window must be holding a reference, so we can return a weak
   // pointer.
   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global);
   return static_cast<nsGlobalWindow*>(win.get());
 }
 
-/**
- * Class used to represent events generated by calls to Window.postMessage,
- * which asynchronously creates and dispatches events.
- */
-class PostMessageEvent : public nsRunnable
-{
-  public:
-    NS_DECL_NSIRUNNABLE
-
-    PostMessageEvent(nsGlobalWindow* aSource,
-                     const nsAString& aCallerOrigin,
-                     nsGlobalWindow* aTargetWindow,
-                     nsIPrincipal* aProvidedPrincipal,
-                     bool aTrustedCaller)
-    : mSource(aSource),
-      mCallerOrigin(aCallerOrigin),
-      mTargetWindow(aTargetWindow),
-      mProvidedPrincipal(aProvidedPrincipal),
-      mTrustedCaller(aTrustedCaller)
-    {
-      MOZ_COUNT_CTOR(PostMessageEvent);
-    }
-
-protected:
-    ~PostMessageEvent()
-    {
-      MOZ_COUNT_DTOR(PostMessageEvent);
-    }
-
-public:
-    JSAutoStructuredCloneBuffer& Buffer()
-    {
-      return mBuffer;
-    }
-
-    bool StoreISupports(nsISupports* aSupports)
-    {
-      mSupportsArray.AppendElement(aSupports);
-      return true;
-    }
-
-  private:
-    JSAutoStructuredCloneBuffer mBuffer;
-    nsRefPtr<nsGlobalWindow> mSource;
-    nsString mCallerOrigin;
-    nsRefPtr<nsGlobalWindow> mTargetWindow;
-    nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
-    bool mTrustedCaller;
-    nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
-};
-
-namespace {
-
-struct StructuredCloneInfo {
-  PostMessageEvent* event;
-  bool subsumes;
-  nsPIDOMWindow* window;
-  nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> ports;
-};
-
-static JSObject*
-PostMessageReadStructuredClone(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");
-
-    nsISupports* supports;
-    if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
-      JS::Rooted<JS::Value> val(cx);
-      if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
-        return val.toObjectOrNull();
-      }
-    }
-  }
-
-  const JSStructuredCloneCallbacks* runtimeCallbacks =
-    js::GetContextStructuredCloneCallbacks(cx);
-
-  if (runtimeCallbacks) {
-    return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
-  }
-
-  return nullptr;
-}
-
-static bool
-PostMessageWriteStructuredClone(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 (scInfo->subsumes && 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;
-      }
-    }
-  }
-
-  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
-  nsContentUtils::XPConnect()->
-    GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
-  if (wrappedNative) {
-    uint32_t scTag = 0;
-    nsISupports* supports = wrappedNative->Native();
-
-    nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
-    if (list && scInfo->subsumes)
-      scTag = SCTAG_DOM_FILELIST;
-
-    if (scTag)
-      return JS_WriteUint32Pair(writer, scTag, 0) &&
-             JS_WriteBytes(writer, &supports, sizeof(supports)) &&
-             scInfo->event->StoreISupports(supports);
-  }
-
-  const JSStructuredCloneCallbacks* runtimeCallbacks =
-    js::GetContextStructuredCloneCallbacks(cx);
-
-  if (runtimeCallbacks) {
-    return runtimeCallbacks->write(cx, writer, obj, nullptr);
-  }
-
-  return false;
-}
-
-static bool
-PostMessageReadTransferStructuredClone(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) {
-    MessagePort* port = static_cast<MessagePort*>(aData);
-    port->BindToOwner(scInfo->window);
-    scInfo->ports.Put(port, nullptr);
-
-    JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx, nullptr));
-    if (JS_WrapObject(aCx, &obj)) {
-      MOZ_ASSERT(port->GetOwner() == scInfo->window);
-      returnObject.set(obj);
-    }
-
-    return true;
-  }
-
-  return false;
-}
-
-static bool
-PostMessageTransferStructuredClone(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)) {
-    nsRefPtr<MessagePortBase> newPort;
-    if (scInfo->ports.Get(port, getter_AddRefs(newPort))) {
-      // No duplicate.
-      return false;
-    }
-
-    newPort = port->Clone();
-    scInfo->ports.Put(port, newPort);
-
-    *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
-    *aOwnership = JS::SCTAG_TMO_CUSTOM;
-    *aContent = newPort;
-    *aExtraData = 0;
-
-    return true;
-  }
-
-  return false;
-}
-
-void
-PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
-                                       void *aContent, uint64_t aExtraData, void* aClosure)
-{
-  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
-  NS_ASSERTION(scInfo, "Must have scInfo!");
-
-  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
-    nsRefPtr<MessagePortBase> port(static_cast<MessagePort*>(aContent));
-    scInfo->ports.Remove(port);
-  }
-}
-
-const JSStructuredCloneCallbacks kPostMessageCallbacks = {
-  PostMessageReadStructuredClone,
-  PostMessageWriteStructuredClone,
-  nullptr,
-  PostMessageReadTransferStructuredClone,
-  PostMessageTransferStructuredClone,
-  PostMessageFreeTransferStructuredClone
-};
-
-} // anonymous namespace
-
-static PLDHashOperator
-PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
-{
-  nsTArray<nsRefPtr<MessagePortBase> > *array =
-    static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
-
-  array->AppendElement(aKey);
-  return PL_DHASH_NEXT;
-}
-
-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!");
-
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-
-  // If we bailed before this point we're going to leak mMessage, but
-  // that's probably better than crashing.
-
-  nsRefPtr<nsGlobalWindow> targetWindow;
-  if (mTargetWindow->IsClosedOrClosing() ||
-      !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
-      targetWindow->IsClosedOrClosing())
-    return NS_OK;
-
-  MOZ_ASSERT(targetWindow->IsInnerWindow(),
-             "we ordered an inner window!");
-  JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
-
-  // Ensure that any origin which might have been provided is the origin of this
-  // window's document.  Note that we do this *now* instead of when postMessage
-  // is called because the target window might have been navigated to a
-  // different location between then and now.  If this check happened when
-  // postMessage was called, it would be fairly easy for a malicious webpage to
-  // intercept messages intended for another site by carefully timing navigation
-  // of the target window so it changed location after postMessage but before
-  // now.
-  if (mProvidedPrincipal) {
-    // Get the target's origin either from its principal or, in the case the
-    // principal doesn't carry a URI (e.g. the system principal), the target's
-    // document.
-    nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
-    if (NS_WARN_IF(!targetPrin))
-      return NS_OK;
-
-    // Note: This is contrary to the spec with respect to file: URLs, which
-    //       the spec groups into a single origin, but given we intentionally
-    //       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, &kPostMessageCallbacks, &scInfo)) {
-    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);
-
-  nsTArray<nsRefPtr<MessagePortBase> > ports;
-  scInfo.ports.EnumerateRead(PopulateMessagePortList, &ports);
-  event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
-
-  // 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->mDoc->GetShell();
-  nsRefPtr<nsPresContext> presContext;
-  if (shell)
-    presContext = shell->GetPresContext();
-
-  event->SetTrusted(mTrustedCaller);
-  WidgetEvent* internalEvent = event->GetInternalNSEvent();
-
-  nsEventStatus status = nsEventStatus_eIgnore;
-  EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
-                            presContext,
-                            internalEvent,
-                            static_cast<dom::Event*>(event.get()),
-                            &status);
-  return NS_OK;
-}
-
 void
 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                const nsAString& aTargetOrigin,
                                JS::Handle<JS::Value> aTransfer,
                                ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(PostMessageMoz,
                             (aCx, aMessage, aTargetOrigin, aTransfer, aError),
@@ -8517,28 +8150,23 @@ nsGlobalWindow::PostMessageMoz(JSContext
     new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
                          ? nullptr
                          : callerInnerWin->GetOuterWindowInternal(),
                          origin,
                          this,
                          providedPrincipal,
                          nsContentUtils::IsCallerChrome());
 
-  // We *must* clone the data here, or the JS::Value could be modified
-  // by script
-  StructuredCloneInfo scInfo;
-  scInfo.event = event;
-  scInfo.window = this;
-
   nsIPrincipal* principal = GetPrincipal();
   JS::Rooted<JS::Value> message(aCx, aMessage);
   JS::Rooted<JS::Value> transfer(aCx, aTransfer);
-  if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)) ||
-      !event->Buffer().write(aCx, message, transfer, &kPostMessageCallbacks,
-                             &scInfo)) {
+  bool subsumes;
+
+  if (NS_FAILED(callerPrin->Subsumes(principal, &subsumes)) ||
+      !event->Write(aCx, message, transfer, subsumes, this)) {
     aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   aError = NS_DispatchToCurrentThread(event);
 }
 
 void
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -106,16 +106,17 @@ class External;
 class Function;
 class Gamepad;
 class VRDevice;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
 class OwningExternalOrWindowProxy;
 class Promise;
+class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
 class Selection;
 class SpeechSynthesis;
 class WakeLock;
 namespace cache {
 class CacheStorage;
 } // namespace cache
@@ -1747,17 +1748,17 @@ protected:
   bool                                       mVRDevicesInitialized;
   // The VRDevies for this window
   nsTArray<nsRefPtr<mozilla::dom::VRDevice>> mVRDevices;
   // Any attached HMD when fullscreen
   nsRefPtr<mozilla::gfx::VRHMDInfo>          mVRHMDInfo;
 
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
-  friend class PostMessageEvent;
+  friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
 
   static WindowByIdTable* sWindowsById;
   static bool sWarnedAboutWindowInternal;
 };
 
 inline nsISupports*
 ToSupports(nsGlobalWindow *p)
--- a/dom/base/test/chrome.ini
+++ b/dom/base/test/chrome.ini
@@ -8,17 +8,16 @@ support-files =
   file_bug1008126_worker.js
 
 [test_anonymousContent_xul_window.xul]
 [test_bug715041.xul]
 [test_bug715041_removal.xul]
 [test_domrequesthelper.xul]
 [test_url.xul]
 [test_console.xul]
-[test_messageChannel.xul]
 [test_navigator_resolve_identity_xrays.xul]
 [test_sendQueryContentAndSelectionSetEvent.html]
 [test_bug1016960.html]
 [test_bug357450.js]
 [test_copypaste.xul]
 [test_messagemanager_principal.html]
 [test_messagemanager_send_principal.html]
 skip-if = buildapp == 'mulet'
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -1,18 +1,14 @@
 [DEFAULT]
 support-files =
   audio.ogg
   iframe_bug976673.html
   iframe_main_bug1022229.html
   iframe_sandbox_bug1022229.html
-  iframe_messageChannel_cloning.html
-  iframe_messageChannel_chrome.html
-  iframe_messageChannel_pingpong.html
-  iframe_messageChannel_post.html
   file_empty.html
   iframe_postMessage_solidus.html
   file_setname.html
   345339_iframe.html
   Ahem.ttf
   accesscontrol.resource
   accesscontrol.resource^headers^
   badContentType.eventsource
@@ -280,25 +276,17 @@ skip-if = buildapp == 'mulet' || buildap
 [test_gsp-standards.html]
 [test_getFeature_with_perm.html]
 [test_getFeature_without_perm.html]
 [test_hasFeature.html]
 [test_history_document_open.html]
 [test_history_state_null.html]
 [test_Image_constructor.html]
 [test_innersize_scrollport.html]
-[test_messageChannel.html]
-[test_messageChannel_cloning.html]
-[test_messageChannel_pingpong.html]
-[test_messageChannel_post.html]
-[test_messageChannel_pref.html]
-[test_messageChannel_start.html]
 [test_messagemanager_targetchain.html]
-[test_messageChannel_transferable.html]
-[test_messageChannel_unshipped.html]
 [test_named_frames.html]
 [test_navigator_resolve_identity.html]
 [test_navigator_language.html]
 [test_openDialogChromeOnly.html]
 [test_open_null_features.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
 [test_postMessage_solidus.html]
 [test_screen_orientation.html]
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -724,19 +724,16 @@ DOMInterfaces = {
 
 'MediaRecorder': {
     'headerFile': 'MediaRecorder.h',
 },
 
 'MessagePort': {
     'nativeType': 'mozilla::dom::MessagePortBase',
     'headerFile': 'mozilla/dom/MessagePort.h',
-    'binaryNames': {
-        'postMessage': 'postMessageMoz',
-    },
 },
 
 'MimeType': {
     'headerFile' : 'nsMimeTypeArray.h',
     'nativeType': 'nsMimeType',
 },
 
 'MimeTypeArray': {
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -207,24 +207,25 @@ public:
 
     SerializedStructuredCloneBuffer& buffer = message.data();
     buffer.data = mData->mBuffer.data();
     buffer.dataLength = mData->mBuffer.nbytes();
 
     PBackgroundChild* backgroundManager = mActor->Manager();
     MOZ_ASSERT(backgroundManager);
 
-    const nsTArray<nsRefPtr<Blob>>& blobs = mData->mClosure.mBlobs;
+    const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = mData->mClosure.mBlobImpls;
+
+    if (!blobImpls.IsEmpty()) {
+      message.blobsChild().SetCapacity(blobImpls.Length());
 
-    if (!blobs.IsEmpty()) {
-      message.blobsChild().SetCapacity(blobs.Length());
-
-      for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
+      for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
         PBlobChild* blobChild =
-          BackgroundChild::GetOrCreateActorForBlob(backgroundManager, blobs[i]);
+          BackgroundChild::GetOrCreateActorForBlobImpl(backgroundManager,
+                                                       blobImpls[i]);
         MOZ_ASSERT(blobChild);
 
         message.blobsChild().AppendElement(blobChild);
       }
     }
 
     mActor->SendPostMessage(message);
     return NS_OK;
@@ -536,19 +537,19 @@ BroadcastChannel::PostMessageInternal(JS
 {
   nsRefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
 
   if (!WriteStructuredClone(aCx, aMessage, data->mBuffer, data->mClosure)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  const nsTArray<nsRefPtr<Blob>>& blobs = data->mClosure.mBlobs;
-  for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
-    if (!blobs[i]->Impl()->MayBeClonedToOtherThreads()) {
+  const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->mClosure.mBlobImpls;
+  for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
+    if (!blobImpls[i]->MayBeClonedToOtherThreads()) {
       aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
       return;
     }
   }
 
   PostMessageData(data);
 }
 
--- a/dom/broadcastchannel/BroadcastChannelChild.cpp
+++ b/dom/broadcastchannel/BroadcastChannelChild.cpp
@@ -37,26 +37,25 @@ BroadcastChannelChild::~BroadcastChannel
   MOZ_ASSERT(!mBC);
 }
 
 bool
 BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
 {
   // Make sure to retrieve all blobs from the message before returning to avoid
   // leaking their actors.
-  nsTArray<nsRefPtr<Blob>> blobs;
+  nsTArray<nsRefPtr<BlobImpl>> blobs;
   if (!aData.blobsChild().IsEmpty()) {
     blobs.SetCapacity(aData.blobsChild().Length());
 
     for (uint32_t i = 0, len = aData.blobsChild().Length(); i < len; ++i) {
       nsRefPtr<BlobImpl> impl =
         static_cast<BlobChild*>(aData.blobsChild()[i])->GetBlobImpl();
 
-      nsRefPtr<Blob> blob = Blob::Create(mBC ? mBC->GetOwner() : nullptr, impl);
-      blobs.AppendElement(blob);
+      blobs.AppendElement(impl);
     }
   }
 
   nsCOMPtr<DOMEventTargetHelper> helper = mBC;
   nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(helper);
 
   // This object has been already closed by content or is going to be deleted
   // soon. No notify is required.
@@ -87,17 +86,17 @@ BroadcastChannelChild::RecvNotify(const 
   }
 
   JSContext* cx = jsapi.cx();
 
   const SerializedStructuredCloneBuffer& buffer = aData.data();
   StructuredCloneData cloneData;
   cloneData.mData = buffer.data;
   cloneData.mDataLength = buffer.dataLength;
-  cloneData.mClosure.mBlobs.SwapElements(blobs);
+  cloneData.mClosure.mBlobImpls.SwapElements(blobs);
 
   JS::Rooted<JS::Value> value(cx, JS::NullValue());
   if (cloneData.mDataLength && !ReadStructuredClone(cx, cloneData, &value)) {
     JS_ClearPendingException(cx);
     return false;
   }
 
   RootedDictionary<MessageEventInit> init(cx);
--- a/dom/ipc/StructuredCloneUtils.cpp
+++ b/dom/ipc/StructuredCloneUtils.cpp
@@ -45,33 +45,33 @@ Read(JSContext* aCx, JSStructuredCloneRe
   if (aTag == SCTAG_DOM_BLOB) {
     // 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);
     {
-      MOZ_ASSERT(aData < closure->mBlobs.Length());
-      nsRefPtr<Blob> blob = closure->mBlobs[aData];
+      MOZ_ASSERT(aData < closure->mBlobImpls.Length());
+      nsRefPtr<BlobImpl> blobImpl = closure->mBlobImpls[aData];
 
 #ifdef DEBUG
       {
         // Blob should not be mutable.
         bool isMutable;
-        MOZ_ASSERT(NS_SUCCEEDED(blob->GetMutable(&isMutable)));
+        MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
         MOZ_ASSERT(!isMutable);
       }
 #endif
 
       // Let's create a new blob with the correct parent.
       nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
       MOZ_ASSERT(global);
 
-      nsRefPtr<Blob> newBlob = Blob::Create(global, blob->Impl());
+      nsRefPtr<Blob> newBlob = Blob::Create(global, blobImpl);
       if (!ToJSValue(aCx, newBlob, &val)) {
         return nullptr;
       }
     }
 
     return &val.toObject();
   }
 
@@ -88,18 +88,18 @@ Write(JSContext* aCx, JSStructuredCloneW
     static_cast<StructuredCloneClosure*>(aClosure);
 
   // See if the wrapped native is a File/Blob.
   {
     Blob* blob = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
         NS_SUCCEEDED(blob->SetMutable(false)) &&
         JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
-                           closure->mBlobs.Length())) {
-      closure->mBlobs.AppendElement(blob);
+                           closure->mBlobImpls.Length())) {
+      closure->mBlobImpls.AppendElement(blob->Impl());
       return true;
     }
   }
 
   return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
 }
 
 const JSStructuredCloneCallbacks gCallbacks = {
--- a/dom/ipc/StructuredCloneUtils.h
+++ b/dom/ipc/StructuredCloneUtils.h
@@ -14,17 +14,17 @@
 #include "js/StructuredClone.h"
 
 namespace mozilla {
 namespace dom {
 
 struct
 StructuredCloneClosure
 {
-  nsTArray<nsRefPtr<Blob>> mBlobs;
+  nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
 };
 
 struct
 StructuredCloneData
 {
   StructuredCloneData() : mData(nullptr), mDataLength(0) {}
   uint64_t* mData;
   size_t mDataLength;
rename from dom/base/MessageChannel.cpp
rename to dom/messagechannel/MessageChannel.cpp
--- a/dom/base/MessageChannel.cpp
+++ b/dom/messagechannel/MessageChannel.cpp
@@ -4,101 +4,225 @@
  * 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 "MessageChannel.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/MessageChannelBinding.h"
 #include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/Navigator.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRunnable.h"
 #include "nsContentUtils.h"
+#include "nsIDocument.h"
+#include "nsIPrincipal.h"
 #include "nsPIDOMWindow.h"
+#include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MessageChannel, mWindow, mPort1, mPort2)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MessageChannel)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MessageChannel)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessageChannel)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 namespace {
-  bool gPrefInitialized = false;
-  bool gPrefEnabled = false;
+bool gPrefInitialized = false;
+bool gPrefEnabled = false;
 
-}
+bool
+CheckPermission(nsIPrincipal* aPrincipal, bool aCallerChrome)
+{
+  MOZ_ASSERT(NS_IsMainThread());
 
-/* static */ bool
-MessageChannel::Enabled(JSContext* aCx, JSObject* aObj)
-{
   if (!gPrefInitialized) {
     Preferences::AddBoolVarCache(&gPrefEnabled, "dom.messageChannel.enabled");
     gPrefInitialized = true;
   }
 
   // Enabled by pref
   if (gPrefEnabled) {
     return true;
   }
 
   // Chrome callers are allowed.
-  if (nsContentUtils::ThreadsafeIsCallerChrome()) {
+  if (aCallerChrome) {
     return true;
   }
 
-  nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal();
-  MOZ_ASSERT(principal);
-
   nsCOMPtr<nsIURI> uri;
-  if (NS_FAILED(principal->GetURI(getter_AddRefs(uri))) || !uri) {
+  if (NS_FAILED(aPrincipal->GetURI(getter_AddRefs(uri))) || !uri) {
     return false;
   }
 
   bool isResource = false;
   if (NS_FAILED(uri->SchemeIs("resource", &isResource))) {
     return false;
   }
 
   return isResource;
 }
 
+nsIPrincipal*
+GetPrincipalFromWorkerPrivate(workers::WorkerPrivate* aWorkerPrivate)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsIPrincipal* principal = aWorkerPrivate->GetPrincipal();
+  if (principal) {
+    return principal;
+  }
+
+  // Walk up to our containing page
+  workers::WorkerPrivate* wp = aWorkerPrivate;
+  while (wp->GetParent()) {
+    wp = wp->GetParent();
+  }
+
+  nsPIDOMWindow* window = wp->GetWindow();
+  if (!window) {
+    return nullptr;
+  }
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (!doc) {
+    return nullptr;
+  }
+
+  return doc->NodePrincipal();
+}
+
+// A WorkerMainThreadRunnable to synchronously dispatch the call of
+// CheckPermission() from the worker thread to the main thread.
+class CheckPermissionRunnable final : public workers::WorkerMainThreadRunnable
+{
+public:
+  bool mResult;
+  bool mCallerChrome;
+
+  explicit CheckPermissionRunnable(workers::WorkerPrivate* aWorkerPrivate)
+    : workers::WorkerMainThreadRunnable(aWorkerPrivate)
+    , mResult(false)
+    , mCallerChrome(false)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    mCallerChrome = aWorkerPrivate->UsesSystemPrincipal();
+  }
+
+protected:
+  virtual bool
+  MainThreadRun() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsIPrincipal* principal = GetPrincipalFromWorkerPrivate(mWorkerPrivate);
+    if (!principal) {
+      return true;
+    }
+
+    bool isNullPrincipal;
+    nsresult rv = principal->GetIsNullPrincipal(&isNullPrincipal);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+       return true;
+    }
+
+    if (NS_WARN_IF(isNullPrincipal)) {
+      return true;
+    }
+
+    mResult = CheckPermission(principal, mCallerChrome);
+    return true;
+  }
+};
+
+} // anonymous namespace
+
+/* static */ bool
+MessageChannel::Enabled(JSContext* aCx, JSObject* aGlobal)
+{
+  if (NS_IsMainThread()) {
+    JS::Rooted<JSObject*> global(aCx, aGlobal);
+
+    nsCOMPtr<nsPIDOMWindow> win = Navigator::GetWindowFromGlobal(global);
+    if (!win) {
+      return false;
+    }
+
+    nsIDocument* doc = win->GetExtantDoc();
+    if (!doc) {
+      return false;
+    }
+
+    return CheckPermission(doc->NodePrincipal(),
+                           nsContentUtils::IsCallerChrome());
+  }
+
+  workers::WorkerPrivate* workerPrivate =
+    workers::GetWorkerPrivateFromContext(aCx);
+  workerPrivate->AssertIsOnWorkerThread();
+
+  nsRefPtr<CheckPermissionRunnable> runnable =
+    new CheckPermissionRunnable(workerPrivate);
+  runnable->Dispatch(aCx);
+
+  return runnable->mResult;
+}
+
 MessageChannel::MessageChannel(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
 {
-  MOZ_COUNT_CTOR(MessageChannel);
-
-  mPort1 = new MessagePort(mWindow);
-  mPort2 = new MessagePort(mWindow);
-
-  mPort1->Entangle(mPort2);
-  mPort2->Entangle(mPort1);
 }
 
 MessageChannel::~MessageChannel()
 {
-  MOZ_COUNT_DTOR(MessageChannel);
 }
 
 JSObject*
 MessageChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MessageChannelBinding::Wrap(aCx, this, aGivenProto);
 }
 
 /* static */ already_AddRefed<MessageChannel>
 MessageChannel::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
+  // window can be null in workers.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  if (!window) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
+
+  nsID portUUID1;
+  aRv = nsContentUtils::GenerateUUIDInPlace(portUUID1);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsID portUUID2;
+  aRv = nsContentUtils::GenerateUUIDInPlace(portUUID2);
+  if (aRv.Failed()) {
     return nullptr;
   }
 
   nsRefPtr<MessageChannel> channel = new MessageChannel(window);
+
+  channel->mPort1 = MessagePort::Create(window, portUUID1, portUUID2, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  channel->mPort2 = MessagePort::Create(window, portUUID2, portUUID1, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  channel->mPort1->UnshippedEntangle(channel->mPort2);
+  channel->mPort2->UnshippedEntangle(channel->mPort1);
+
   return channel.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
rename from dom/base/MessageChannel.h
rename to dom/messagechannel/MessageChannel.h
--- a/dom/base/MessageChannel.h
+++ b/dom/messagechannel/MessageChannel.h
@@ -26,18 +26,16 @@ class MessageChannel final : public nsIS
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MessageChannel)
 
   static bool Enabled(JSContext* aCx, JSObject* aGlobal);
 
 public:
-  explicit MessageChannel(nsPIDOMWindow* aWindow);
-
   nsPIDOMWindow*
   GetParentObject() const
   {
     return mWindow;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -53,16 +51,17 @@ public:
 
   MessagePort*
   Port2() const
   {
     return mPort2;
   }
 
 private:
+  explicit MessageChannel(nsPIDOMWindow* aWindow);
   ~MessageChannel();
 
   nsCOMPtr<nsPIDOMWindow> mWindow;
 
   nsRefPtr<MessagePort> mPort1;
   nsRefPtr<MessagePort> mPort2;
 };
 
rename from dom/base/MessagePort.cpp
rename to dom/messagechannel/MessagePort.cpp
--- a/dom/base/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -1,516 +1,531 @@
 /* -*- 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 "MessagePort.h"
+
 #include "MessageEvent.h"
+#include "MessagePortChild.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessageChannel.h"
 #include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/MessagePortList.h"
+#include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerScope.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsPresContext.h"
 #include "ScriptSettings.h"
+#include "SharedMessagePortMessage.h"
 
+#include "nsIBFCacheEntry.h"
 #include "nsIDocument.h"
 #include "nsIDOMFileList.h"
 #include "nsIPresShell.h"
+#include "nsISupportsPrimitives.h"
+#include "nsServiceManagerUtils.h"
+
+#ifdef XP_WIN
+#undef PostMessage
+#endif
+
+using namespace mozilla::dom::workers;
 
 namespace mozilla {
 namespace dom {
 
-class DispatchEventRunnable : public nsRunnable
-{
-  friend class MessagePort;
-
-  public:
-    explicit DispatchEventRunnable(MessagePort* aPort)
-      : mPort(aPort)
-    {
-    }
-
-    NS_IMETHOD
-    Run()
-    {
-      nsRefPtr<DispatchEventRunnable> mKungFuDeathGrip(this);
-
-      mPort->mDispatchRunnable = nullptr;
-      mPort->Dispatch();
-
-      return NS_OK;
-    }
-
-  private:
-    nsRefPtr<MessagePort> mPort;
-};
-
-class PostMessageRunnable : public nsRunnable
+class DispatchEventRunnable final : public nsICancelableRunnable
 {
   friend class MessagePort;
 
-  public:
-    NS_DECL_NSIRUNNABLE
+public:
+  NS_DECL_ISUPPORTS
 
-    PostMessageRunnable()
-    {
-    }
+  explicit DispatchEventRunnable(MessagePort* aPort)
+    : mPort(aPort)
+  { }
 
-    ~PostMessageRunnable()
-    {
-    }
-
-    JSAutoStructuredCloneBuffer& Buffer()
-    {
-      return mBuffer;
-    }
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(mPort);
+    MOZ_ASSERT(mPort->mDispatchRunnable == this);
+    mPort->mDispatchRunnable = nullptr;
+    mPort->Dispatch();
 
-    bool StoreISupports(nsISupports* aSupports)
-    {
-      mSupportsArray.AppendElement(aSupports);
-      return true;
-    }
-
-    void Dispatch(MessagePort* aPort)
-    {
-      mPort = aPort;
-      NS_DispatchToCurrentThread(this);
-    }
+    return NS_OK;
+  }
 
-  private:
-    nsRefPtr<MessagePort> mPort;
-    JSAutoStructuredCloneBuffer mBuffer;
-
-    nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
-};
+  NS_IMETHOD
+  Cancel() override
+  {
+    mPort = nullptr;
+    return NS_OK;
+  }
 
-namespace {
+private:
+  ~DispatchEventRunnable()
+  {}
 
-struct StructuredCloneInfo
-{
-  PostMessageRunnable* mEvent;
-  MessagePort* mPort;
-  nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> mPorts;
+  nsRefPtr<MessagePort> mPort;
 };
 
-static JSObject*
-PostMessageReadStructuredClone(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);
+NS_IMPL_ISUPPORTS(DispatchEventRunnable, nsICancelableRunnable, nsIRunnable)
 
-      // 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->mPort->GetParentObject(),
-                                           blobImpl);
-        if (!ToJSValue(cx, blob, &val)) {
-          return nullptr;
-        }
-      }
-
-      return &val.toObject();
-    }
-  }
-
-  if (tag == SCTAG_DOM_FILELIST) {
-    NS_ASSERTION(!data, "Data should be empty");
-
-    nsISupports* supports;
-    if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
-      JS::Rooted<JS::Value> val(cx);
-      if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
-        return val.toObjectOrNull();
-      }
-    }
-  }
+class PostMessageRunnable final : public nsICancelableRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
 
-  const JSStructuredCloneCallbacks* runtimeCallbacks =
-    js::GetContextStructuredCloneCallbacks(cx);
-
-  if (runtimeCallbacks) {
-    return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
-  }
-
-  return nullptr;
-}
-
-static bool
-PostMessageWriteStructuredClone(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.
+  PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
+    : mPort(aPort)
+    , mData(aData)
   {
-    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->mEvent->StoreISupports(blobImpl);
-        return true;
-      }
-    }
-  }
-
-  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
-  nsContentUtils::XPConnect()->
-    GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
-  if (wrappedNative) {
-    uint32_t scTag = 0;
-    nsISupports* supports = wrappedNative->Native();
-
-    nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
-    if (list) {
-      scTag = SCTAG_DOM_FILELIST;
-    }
-
-    if (scTag) {
-      return JS_WriteUint32Pair(writer, scTag, 0) &&
-             JS_WriteBytes(writer, &supports, sizeof(supports)) &&
-             scInfo->mEvent->StoreISupports(supports);
-    }
+    MOZ_ASSERT(aPort);
+    MOZ_ASSERT(aData);
   }
 
-  const JSStructuredCloneCallbacks* runtimeCallbacks =
-    js::GetContextStructuredCloneCallbacks(cx);
-
-  if (runtimeCallbacks) {
-    return runtimeCallbacks->write(cx, writer, obj, nullptr);
-  }
-
-  return false;
-}
+  NS_IMETHOD
+  Run() override
+  {
+    nsCOMPtr<nsIGlobalObject> globalObject;
 
-static bool
-PostMessageReadTransferStructuredClone(JSContext* aCx,
-                                       JSStructuredCloneReader* reader,
-                                       uint32_t tag, void* data,
-                                       uint64_t unused,
-                                       void* aClosure,
-                                       JS::MutableHandle<JSObject*> returnObject)
-{
-  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
-  NS_ASSERTION(scInfo, "Must have scInfo!");
+    if (NS_IsMainThread()) {
+      globalObject = do_QueryInterface(mPort->GetParentObject());
+    } else {
+      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+      MOZ_ASSERT(workerPrivate);
+      globalObject = workerPrivate->GlobalScope();
+    }
 
-  if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
-    MessagePort* port = static_cast<MessagePort*>(data);
-    port->BindToOwner(scInfo->mPort->GetOwner());
-    scInfo->mPorts.Put(port, nullptr);
-
-    JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx, nullptr));
-    if (!obj || !JS_WrapObject(aCx, &obj)) {
-      return false;
+    AutoJSAPI jsapi;
+    if (!globalObject || !jsapi.Init(globalObject)) {
+      NS_WARNING("Failed to initialize AutoJSAPI object.");
+      return NS_ERROR_FAILURE;
     }
 
-    MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner());
-    returnObject.set(obj);
-    return true;
-  }
+    JSContext* cx = jsapi.cx();
 
-  return false;
-}
+    nsTArray<nsRefPtr<MessagePort>> ports;
+    nsCOMPtr<nsPIDOMWindow> window =
+      do_QueryInterface(mPort->GetParentObject());
 
-static bool
-PostMessageTransferStructuredClone(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!");
+    JS::Rooted<JS::Value> value(cx);
+    if (!mData->mData.IsEmpty()) {
+      bool ok = ReadStructuredCloneWithTransfer(cx, mData->mData,
+                                                mData->mClosure,
+                                                &value, window, ports);
+      FreeStructuredClone(mData->mData, mData->mClosure);
 
-  MessagePortBase *port = nullptr;
-  nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
-  if (NS_SUCCEEDED(rv)) {
-    nsRefPtr<MessagePortBase> newPort;
-    if (scInfo->mPorts.Get(port, getter_AddRefs(newPort))) {
-      // No duplicate.
-      return false;
+      if (!ok) {
+        return NS_ERROR_FAILURE;
+      }
     }
 
-    newPort = port->Clone();
-    scInfo->mPorts.Put(port, newPort);
+    // The data should be already be cleaned.
+    MOZ_ASSERT(!mData->mData.Length());
+
+    // Create the event
+    nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
+      do_QueryInterface(mPort->GetOwner());
+    nsRefPtr<MessageEvent> event =
+      new MessageEvent(eventTarget, nullptr, nullptr);
 
-    *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
-    *aOwnership = JS::SCTAG_TMO_CUSTOM;
-    *aContent = newPort;
-    *aExtraData = 0;
+    event->InitMessageEvent(NS_LITERAL_STRING("message"),
+                            false /* non-bubbling */,
+                            false /* cancelable */, value, EmptyString(),
+                            EmptyString(), nullptr);
+    event->SetTrusted(true);
+    event->SetSource(mPort);
 
-    return true;
+    nsTArray<nsRefPtr<MessagePortBase>> array;
+    array.SetCapacity(ports.Length());
+    for (uint32_t i = 0; i < ports.Length(); ++i) {
+      array.AppendElement(ports[i]);
+    }
+
+    nsRefPtr<MessagePortList> portList =
+      new MessagePortList(static_cast<dom::Event*>(event.get()), array);
+    event->SetPorts(portList);
+
+    bool dummy;
+    mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
+    return NS_OK;
   }
 
-  return false;
-}
-
-static void
-PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
-                                       void* aData,
-                                       uint64_t aExtraData,
-                                       void* aClosure)
-{
-  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
-  NS_ASSERTION(scInfo, "Must have scInfo!");
+  NS_IMETHOD
+  Cancel() override
+  {
+    mPort = nullptr;
+    mData = nullptr;
+    return NS_OK;
+  }
 
-  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
-    MOZ_ASSERT(aOwnership == JS::SCTAG_TMO_CUSTOM);
-    nsRefPtr<MessagePort> port(static_cast<MessagePort*>(aData));
-    scInfo->mPorts.Remove(port);
-  }
-}
+private:
+  ~PostMessageRunnable()
+  {}
 
-const JSStructuredCloneCallbacks kPostMessageCallbacks = {
-  PostMessageReadStructuredClone,
-  PostMessageWriteStructuredClone,
-  nullptr,
-  PostMessageReadTransferStructuredClone,
-  PostMessageTransferStructuredClone,
-  PostMessageFreeTransferStructuredClone
+  nsRefPtr<MessagePort> mPort;
+  nsRefPtr<SharedMessagePortMessage> mData;
 };
 
-} // anonymous namespace
-
-static PLDHashOperator
-PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
-{
-  nsTArray<nsRefPtr<MessagePortBase> > *array =
-    static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
-
-  array->AppendElement(aKey);
-  return PL_DHASH_NEXT;
-}
-
-NS_IMETHODIMP
-PostMessageRunnable::Run()
-{
-  MOZ_ASSERT(mPort);
-
-  AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.Init(mPort->GetParentObject()))) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  JSContext* cx = jsapi.cx();
-
-  // Deserialize the structured clone data
-  JS::Rooted<JS::Value> messageData(cx);
-  StructuredCloneInfo scInfo;
-  scInfo.mEvent = this;
-  scInfo.mPort = mPort;
-
-  if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
-    return NS_ERROR_DOM_DATA_CLONE_ERR;
-  }
-
-  // Create the event
-  nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
-    do_QueryInterface(mPort->GetOwner());
-  nsRefPtr<MessageEvent> event =
-    new MessageEvent(eventTarget, nullptr, nullptr);
-
-  event->InitMessageEvent(NS_LITERAL_STRING("message"), false /* non-bubbling */,
-                          false /* cancelable */, messageData, EmptyString(),
-                          EmptyString(), nullptr);
-  event->SetTrusted(true);
-  event->SetSource(mPort);
-
-  nsTArray<nsRefPtr<MessagePortBase> > ports;
-  scInfo.mPorts.EnumerateRead(PopulateMessagePortList, &ports);
-  event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
-
-  bool status;
-  mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &status);
-  return status ? NS_OK : NS_ERROR_FAILURE;
-}
+NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
 
 MessagePortBase::MessagePortBase(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
 {
 }
 
 MessagePortBase::MessagePortBase()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
-                                                DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntangledPort)
-
-  // Custom unlink loop because this array contains nsRunnable objects
-  // which are not cycle colleactable.
-  while (!tmp->mMessageQueue.IsEmpty()) {
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mPort);
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mSupportsArray);
-    tmp->mMessageQueue.RemoveElementAt(0);
-  }
-
+                                                MessagePortBase)
   if (tmp->mDispatchRunnable) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
   }
 
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
-                                                  DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntangledPort)
-
-  // Custom unlink loop because this array contains nsRunnable objects
-  // which are not cycle colleactable.
-  for (uint32_t i = 0, len = tmp->mMessageQueue.Length(); i < len; ++i) {
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mPort);
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mSupportsArray);
-  }
-
+                                                  MessagePortBase)
   if (tmp->mDispatchRunnable) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
   }
 
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
-NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+  NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END_INHERITING(MessagePortBase)
+
+NS_IMPL_ADDREF_INHERITED(MessagePort, MessagePortBase)
+NS_IMPL_RELEASE_INHERITED(MessagePort, MessagePortBase)
+
+namespace {
+
+class MessagePortFeature final : public workers::WorkerFeature
+{
+  MessagePort* mPort;
 
-NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
+public:
+  explicit MessagePortFeature(MessagePort* aPort)
+    : mPort(aPort)
+  {
+    MOZ_ASSERT(aPort);
+    MOZ_COUNT_CTOR(MessagePortFeature);
+  }
+
+  virtual bool Notify(JSContext* aCx, workers::Status aStatus) override
+  {
+    if (mPort && aStatus > Running) {
+      mPort->Close();
+    }
+
+    return true;
+  }
+
+private:
+  ~MessagePortFeature()
+  {
+    MOZ_COUNT_DTOR(MessagePortFeature);
+  }
+};
+
+} // anonymous namespace
 
 MessagePort::MessagePort(nsPIDOMWindow* aWindow)
   : MessagePortBase(aWindow)
+  , mInnerID(0)
   , mMessageQueueEnabled(false)
+  , mIsKeptAlive(false)
 {
+  mIdentifier = new MessagePortIdentifier();
+  mIdentifier->neutered() = true;
+  mIdentifier->sequenceId() = 0;
 }
 
 MessagePort::~MessagePort()
 {
   Close();
+  MOZ_ASSERT(!mWorkerFeature);
+}
+
+/* static */ already_AddRefed<MessagePort>
+MessagePort::Create(nsPIDOMWindow* aWindow, const nsID& aUUID,
+                    const nsID& aDestinationUUID, ErrorResult& aRv)
+{
+  nsRefPtr<MessagePort> mp = new MessagePort(aWindow);
+  mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
+                 false /* Neutered */, eStateUnshippedEntangled, aRv);
+  return mp.forget();
+}
+
+/* static */ already_AddRefed<MessagePort>
+MessagePort::Create(nsPIDOMWindow* aWindow,
+                    const MessagePortIdentifier& aIdentifier,
+                    ErrorResult& aRv)
+{
+  nsRefPtr<MessagePort> mp = new MessagePort(aWindow);
+  mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
+                 aIdentifier.sequenceId(), aIdentifier.neutered(),
+                 eStateEntangling, aRv);
+  return mp.forget();
+}
+
+void
+MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
+{
+  MOZ_ASSERT(aEntangledPort);
+  MOZ_ASSERT(!mUnshippedEntangledPort);
+
+  mUnshippedEntangledPort = aEntangledPort;
+}
+
+void
+MessagePort::Initialize(const nsID& aUUID,
+                        const nsID& aDestinationUUID,
+                        uint32_t aSequenceID, bool mNeutered,
+                        State aState, ErrorResult& aRv)
+{
+  MOZ_ASSERT(mIdentifier);
+  mIdentifier->uuid() = aUUID;
+  mIdentifier->destinationUuid() = aDestinationUUID;
+  mIdentifier->sequenceId() = aSequenceID;
+
+  mState = aState;
+  mNextStep = eNextStepNone;
+
+  if (mNeutered) {
+    mState = eStateDisentangled;
+  } else if (mState == eStateEntangling) {
+    ConnectToPBackground();
+  } else {
+    MOZ_ASSERT(mState == eStateUnshippedEntangled);
+  }
+
+  // The port has to keep itself alive until it's entangled.
+  UpdateMustKeepAlive();
+
+  if (NS_IsMainThread()) {
+    MOZ_ASSERT(GetOwner());
+    MOZ_ASSERT(GetOwner()->IsInnerWindow());
+    mInnerID = GetOwner()->WindowID();
+
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->AddObserver(this, "inner-window-destroyed", false);
+    }
+  } else {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    MOZ_ASSERT(!mWorkerFeature);
+
+    nsAutoPtr<WorkerFeature> feature(new MessagePortFeature(this));
+    JSContext* cx = workerPrivate->GetJSContext();
+    if (NS_WARN_IF(!workerPrivate->AddFeature(cx, feature))) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    mWorkerFeature = Move(feature);
+  }
 }
 
 JSObject*
 MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MessagePortBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
-MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                            const Optional<Sequence<JS::Value>>& aTransferable,
-                            ErrorResult& aRv)
+MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                         const Optional<Sequence<JS::Value>>& aTransferable,
+                         ErrorResult& aRv)
 {
-  nsRefPtr<PostMessageRunnable> event = new PostMessageRunnable();
-
   // We *must* clone the data here, or the JS::Value could be modified
   // by script
-  StructuredCloneInfo scInfo;
-  scInfo.mEvent = event;
-  scInfo.mPort = this;
 
   JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
   if (aTransferable.WasPassed()) {
     const Sequence<JS::Value>& realTransferable = aTransferable.Value();
 
+    // Here we want to check if the transerable object list contains
+    // this port. No other checks are done.
+    for (const JS::Value& value : realTransferable) {
+      if (!value.isObject()) {
+        continue;
+      }
+
+      MessagePortBase* port = nullptr;
+      nsresult rv = UNWRAP_OBJECT(MessagePort, &value.toObject(), port);
+      if (NS_FAILED(rv)) {
+        continue;
+      }
+
+      if (port == this) {
+        aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+        return;
+      }
+    }
+
     // The input sequence only comes from the generated bindings code, which
     // ensures it is rooted.
     JS::HandleValueArray elements =
       JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
                                                realTransferable.Elements());
 
     JSObject* array =
       JS_NewArrayObject(aCx, elements);
     if (!array) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
+
     transferable.setObject(*array);
   }
 
-  if (!event->Buffer().write(aCx, aMessage, transferable,
-                             &kPostMessageCallbacks, &scInfo)) {
+  nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+  if (!WriteStructuredCloneWithTransfer(aCx, aMessage, transferable,
+                                        data->mData, data->mClosure)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  if (!mEntangledPort) {
+  // This message has to be ignored.
+  if (mState > eStateEntangled) {
+    return;
+  }
+
+  // If we are unshipped we are connected to the other port on the same thread.
+  if (mState == eStateUnshippedEntangled) {
+    MOZ_ASSERT(mUnshippedEntangledPort);
+    mUnshippedEntangledPort->mMessages.AppendElement(data);
+    mUnshippedEntangledPort->Dispatch();
+    return;
+  }
+
+  // Not entangled yet, but already closed.
+  if (mNextStep != eNextStepNone) {
     return;
   }
 
-  mEntangledPort->mMessageQueue.AppendElement(event);
-  mEntangledPort->Dispatch();
+  RemoveDocFromBFCache();
+
+  // Not entangled yet.
+  if (mState == eStateEntangling) {
+    mMessagesForTheOtherPort.AppendElement(data);
+    return;
+  }
+
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
+
+  nsAutoTArray<nsRefPtr<SharedMessagePortMessage>, 1> array;
+  array.AppendElement(data);
+
+  nsAutoTArray<MessagePortMessage, 1> messages;
+  SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages);
+  mActor->SendPostMessages(messages);
 }
 
 void
 MessagePort::Start()
 {
   if (mMessageQueueEnabled) {
     return;
   }
 
   mMessageQueueEnabled = true;
   Dispatch();
 }
 
 void
 MessagePort::Dispatch()
 {
-  if (!mMessageQueueEnabled || mMessageQueue.IsEmpty() || mDispatchRunnable) {
+  if (!mMessageQueueEnabled || mMessages.IsEmpty() || mDispatchRunnable ||
+      mState > eStateEntangled || mNextStep != eNextStepNone) {
     return;
   }
 
-  nsRefPtr<PostMessageRunnable> event = mMessageQueue.ElementAt(0);
-  mMessageQueue.RemoveElementAt(0);
+  nsRefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
+  mMessages.RemoveElementAt(0);
 
-  event->Dispatch(this);
+  nsRefPtr<PostMessageRunnable> runnable = new PostMessageRunnable(this, data);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
 
   mDispatchRunnable = new DispatchEventRunnable(this);
-  NS_DispatchToCurrentThread(mDispatchRunnable);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(mDispatchRunnable)));
 }
 
 void
 MessagePort::Close()
 {
-  if (!mEntangledPort) {
+  // Not entangled yet, but already closed.
+  if (mNextStep != eNextStepNone) {
+    return;
+  }
+
+  if (mState == eStateUnshippedEntangled) {
+    MOZ_ASSERT(mUnshippedEntangledPort);
+
+    // This avoids loops.
+    nsRefPtr<MessagePort> port = Move(mUnshippedEntangledPort);
+    MOZ_ASSERT(mUnshippedEntangledPort == nullptr);
+
+    mState = eStateDisentangled;
+    port->Close();
+
+    UpdateMustKeepAlive();
     return;
   }
 
-  // This avoids loops.
-  nsRefPtr<MessagePort> port = mEntangledPort;
-  mEntangledPort = nullptr;
+  // Not entangled yet, we have to wait.
+  if (mState < eStateEntangling) {
+    mNextStep = eNextStepClose;
+    return;
+  }
+
+  if (mState > eStateEntangled) {
+    return;
+  }
 
-  // Let's disentangle the 2 ports symmetrically.
-  port->Close();
+  // We don't care about stopping the sending of messages because from now all
+  // the incoming messages will be ignored.
+  mState = eStateDisentangled;
+
+  MOZ_ASSERT(mActor);
+
+  mActor->SendClose();
+  mActor->SetPort(nullptr);
+  mActor = nullptr;
+
+  UpdateMustKeepAlive();
 }
 
 EventHandlerNonNull*
 MessagePort::GetOnmessage()
 {
   if (NS_IsMainThread()) {
     return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
   }
@@ -525,40 +540,328 @@ MessagePort::SetOnmessage(EventHandlerNo
   } else {
     SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
   }
 
   // When using onmessage, the call to start() is implied.
   Start();
 }
 
+// This method is called when the PMessagePortChild actor is entangled to
+// another actor. It receives a list of messages to be dispatch. It can be that
+// we were waiting for this entangling step in order to disentangle the port or
+// to close it.
 void
-MessagePort::Entangle(MessagePort* aMessagePort)
+MessagePort::Entangled(nsTArray<MessagePortMessage>& aMessages)
+{
+  MOZ_ASSERT(mState == eStateEntangling);
+
+  mState = eStateEntangled;
+
+  // If we have pending messages, these have to be sent.
+  if (!mMessagesForTheOtherPort.IsEmpty()) {
+    nsTArray<MessagePortMessage> messages;
+    SharedMessagePortMessage::FromSharedToMessagesChild(mActor,
+                                                        mMessagesForTheOtherPort,
+                                                        messages);
+    mMessagesForTheOtherPort.Clear();
+    mActor->SendPostMessages(messages);
+  }
+
+  // We must convert the messages into SharedMessagePortMessages to avoid leaks.
+  FallibleTArray<nsRefPtr<SharedMessagePortMessage>> data;
+  if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
+                                                                      data))) {
+    // OOM, we cannot continue.
+    return;
+  }
+
+  if (mNextStep == eNextStepClose) {
+    Close();
+    return;
+  }
+
+  mMessages.AppendElements(data);
+
+  // We were waiting for the entangling callback in order to disentangle this
+  // port immediately after.
+  if (mNextStep == eNextStepDisentangle) {
+    StartDisentangling();
+    return;
+  }
+
+  MOZ_ASSERT(mNextStep == eNextStepNone);
+  Dispatch();
+}
+
+void
+MessagePort::StartDisentangling()
 {
-  MOZ_ASSERT(aMessagePort);
-  MOZ_ASSERT(aMessagePort != this);
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(mState == eStateEntangled);
+
+  mState = eStateDisentangling;
+  mNextStep = eNextStepNone;
+
+  // Sending this message we communicate to the parent actor that we don't want
+  // to receive any new messages. It is possible that a message has been
+  // already sent but not received yet. So we have to collect all of them and
+  // we send them in the SendDispatch() request.
+  mActor->SendStopSendingData();
+}
+
+void
+MessagePort::MessagesReceived(nsTArray<MessagePortMessage>& aMessages)
+{
+  MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling);
+  MOZ_ASSERT(mNextStep == eNextStepNone);
+  MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
+
+  RemoveDocFromBFCache();
+
+  FallibleTArray<nsRefPtr<SharedMessagePortMessage>> data;
+  if (!NS_WARN_IF(SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
+                                                                      data))) {
+    // OOM, We cannot continue.
+    return;
+  }
+
+  mMessages.AppendElements(data);
 
-  Close();
+  if (mState == eStateEntangled) {
+    Dispatch();
+  }
+}
+
+void
+MessagePort::StopSendingDataConfirmed()
+{
+  MOZ_ASSERT(mState == eStateDisentangling);
+  MOZ_ASSERT(mActor);
+
+  Disentangle();
+}
 
-  mEntangledPort = aMessagePort;
+void
+MessagePort::Disentangle()
+{
+  MOZ_ASSERT(mState == eStateDisentangling);
+  MOZ_ASSERT(mActor);
+
+  mState = eStateDisentangled;
+
+  nsTArray<MessagePortMessage> messages;
+  SharedMessagePortMessage::FromSharedToMessagesChild(mActor, mMessages,
+                                                      messages);
+  mMessages.Clear();
+  mActor->SendDisentangle(messages);
+
+  mActor->SetPort(nullptr);
+  mActor = nullptr;
+
+  UpdateMustKeepAlive();
 }
 
-already_AddRefed<MessagePortBase>
-MessagePort::Clone()
+bool
+MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
 {
-  nsRefPtr<MessagePort> newPort = new MessagePort(nullptr);
+  MOZ_ASSERT(mIdentifier);
+
+  // We can clone a port that has already been transfered. In this case, on the
+  // otherside will have a neutered port. Here we set neutered to true so that
+  // we are safe in case a early return.
+  aIdentifier.neutered() = true;
+
+  if (mState > eStateEntangled) {
+    return true;
+  }
+
+  // We already have a 'next step'. We have to consider this port as already
+  // cloned/closed/disentangled.
+  if (mNextStep != eNextStepNone) {
+    return true;
+  }
+
+  aIdentifier.uuid() = mIdentifier->uuid();
+  aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
+  aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
+  aIdentifier.neutered() = false;
+
+  // We have to entangle first.
+  if (mState == eStateUnshippedEntangled) {
+    MOZ_ASSERT(mUnshippedEntangledPort);
+    MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
+
+    // Disconnect the entangled port and connect it to PBackground.
+    mUnshippedEntangledPort->ConnectToPBackground();
+    mUnshippedEntangledPort = nullptr;
 
-  // Move all the events in the port message queue of original port.
-  newPort->mMessageQueue.SwapElements(mMessageQueue);
+    // In this case, we don't need to be connected to the PBackground service.
+    if (mMessages.IsEmpty()) {
+      aIdentifier.sequenceId() = mIdentifier->sequenceId();
+
+      mState = eStateDisentangled;
+      UpdateMustKeepAlive();
+      return true;
+    }
+
+    // Register this component to PBackground.
+    ConnectToPBackground();
+
+    mNextStep = eNextStepDisentangle;
+    return true;
+  }
 
-  if (mEntangledPort) {
-    nsRefPtr<MessagePort> port = mEntangledPort;
-    mEntangledPort = nullptr;
+  // Not entangled yet, we have to wait.
+  if (mState < eStateEntangled) {
+    mNextStep = eNextStepDisentangle;
+    return true;
+  }
+
+  StartDisentangling();
+  return true;
+}
 
-    newPort->Entangle(port);
-    port->Entangle(newPort);
+void
+MessagePort::Closed()
+{
+  if (mState == eStateDisentangled) {
+    return;
+  }
+
+  mState = eStateDisentangled;
+
+  if (mActor) {
+    mActor->SetPort(nullptr);
+    mActor = nullptr;
   }
 
-  return newPort.forget();
+  UpdateMustKeepAlive();
+}
+
+void
+MessagePort::ConnectToPBackground()
+{
+  mState = eStateEntangling;
+
+  PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  if (actor) {
+    ActorCreated(actor);
+  } else {
+    if (NS_WARN_IF(
+        !mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
+      MOZ_CRASH();
+    }
+  }
+}
+
+void
+MessagePort::ActorFailed()
+{
+  MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+}
+
+void
+MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(!mActor);
+  MOZ_ASSERT(mIdentifier);
+  MOZ_ASSERT(mState == eStateEntangling);
+
+  PMessagePortChild* actor =
+    aActor->SendPMessagePortConstructor(mIdentifier->uuid(),
+                                        mIdentifier->destinationUuid(),
+                                        mIdentifier->sequenceId());
+
+  mActor = static_cast<MessagePortChild*>(actor);
+  MOZ_ASSERT(mActor);
+
+  mActor->SetPort(this);
+}
+
+void
+MessagePort::UpdateMustKeepAlive()
+{
+  if (mState == eStateDisentangled && mIsKeptAlive) {
+    mIsKeptAlive = false;
+
+    if (mWorkerFeature) {
+      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+      MOZ_ASSERT(workerPrivate);
+
+      workerPrivate->RemoveFeature(workerPrivate->GetJSContext(),
+                                   mWorkerFeature);
+      mWorkerFeature = nullptr;
+    }
+
+    Release();
+    return;
+  }
+
+  if (mState < eStateDisentangled && !mIsKeptAlive) {
+    mIsKeptAlive = true;
+    AddRef();
+  }
+}
+
+NS_IMETHODIMP
+MessagePort::Observe(nsISupports* aSubject, const char* aTopic,
+                     const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (strcmp(aTopic, "inner-window-destroyed")) {
+    return NS_OK;
+  }
+
+  // If the window id destroyed we have to release the reference that we are
+  // keeping.
+  if (!mIsKeptAlive) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+  NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
+
+  uint64_t innerID;
+  nsresult rv = wrapper->GetData(&innerID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (innerID == mInnerID) {
+    nsCOMPtr<nsIObserverService> obs =
+      do_GetService("@mozilla.org/observer-service;1");
+    if (obs) {
+      obs->RemoveObserver(this, "inner-window-destroyed");
+    }
+
+    Close();
+  }
+
+  return NS_OK;
+}
+
+void
+MessagePort::RemoveDocFromBFCache()
+{
+  if (!NS_IsMainThread()) {
+    return;
+  }
+
+  nsPIDOMWindow* window = GetOwner();
+  MOZ_ASSERT(window);
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (!doc) {
+    return;
+  }
+
+  nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
+  if (!bfCacheEntry) {
+    return;
+  }
+
+  bfCacheEntry->RemoveFromBFCacheSync();
 }
 
 } // namespace dom
 } // namespace mozilla
rename from dom/base/MessagePort.h
rename to dom/messagechannel/MessagePort.h
--- a/dom/base/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -4,112 +4,207 @@
  * 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_MessagePort_h
 #define mozilla_dom_MessagePort_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsTArray.h"
+
+#ifdef XP_WIN
+#undef PostMessage
+#endif
 
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class DispatchEventRunnable;
-class PostMessageRunnable;
+class MessagePortChild;
+class MessagePortIdentifier;
+class MessagePortMessage;
+class SharedMessagePortMessage;
+
+namespace workers {
+class WorkerFeature;
+}
 
 class MessagePortBase : public DOMEventTargetHelper
 {
 protected:
   explicit MessagePortBase(nsPIDOMWindow* aWindow);
   MessagePortBase();
 
 public:
 
   virtual void
-  PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                const Optional<Sequence<JS::Value>>& aTransferable,
-                ErrorResult& aRv) = 0;
+  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const Optional<Sequence<JS::Value>>& aTransferable,
+              ErrorResult& aRv) = 0;
 
   virtual void
   Start() = 0;
 
   virtual void
   Close() = 0;
 
   // The 'message' event handler has to call |Start()| method, so we
   // cannot use IMPL_EVENT_HANDLER macro here.
   virtual EventHandlerNonNull*
   GetOnmessage() = 0;
 
   virtual void
   SetOnmessage(EventHandlerNonNull* aCallback) = 0;
 
   // Duplicate this message port. This method is used by the Structured Clone
-  // Algorithm and makes the new MessagePort active with the entangled
-  // MessagePort of this object.
-  virtual already_AddRefed<MessagePortBase>
-  Clone() = 0;
+  // Algorithm and populates a MessagePortIdentifier object with the information
+  // useful to create new MessagePort.
+  virtual bool
+  CloneAndDisentangle(MessagePortIdentifier& aIdentifier) = 0;
 };
 
 class MessagePort final : public MessagePortBase
+                        , public nsIIPCBackgroundChildCreateCallback
+                        , public nsIObserver
 {
   friend class DispatchEventRunnable;
-  friend class PostMessageRunnable;
 
 public:
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+  NS_DECL_NSIOBSERVER
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
                                            DOMEventTargetHelper)
 
-  explicit MessagePort(nsPIDOMWindow* aWindow);
+  static already_AddRefed<MessagePort>
+  Create(nsPIDOMWindow* aWindow, const nsID& aUUID,
+         const nsID& aDestinationUUID, ErrorResult& aRv);
+
+  static already_AddRefed<MessagePort>
+  Create(nsPIDOMWindow* aWindow, const MessagePortIdentifier& aIdentifier,
+         ErrorResult& aRv);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual void
-  PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                 const Optional<Sequence<JS::Value>>& aTransferable,
-                 ErrorResult& aRv) override;
+  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const Optional<Sequence<JS::Value>>& aTransferable,
+              ErrorResult& aRv) override;
 
-  virtual void
-  Start() override;
+  virtual void Start() override;
 
-  virtual void
-  Close() override;
+  virtual void Close() override;
 
-  virtual EventHandlerNonNull*
-  GetOnmessage() override;
+  virtual EventHandlerNonNull* GetOnmessage() override;
 
-  virtual void
-  SetOnmessage(EventHandlerNonNull* aCallback) override;
+  virtual void SetOnmessage(EventHandlerNonNull* aCallback) override;
 
   // Non WebIDL methods
 
-  // This method entangles this MessagePort with another one.
-  // If it is already entangled, it's disentangled first and enatangle to the
-  // new one.
-  void
-  Entangle(MessagePort* aMessagePort);
+  void UnshippedEntangle(MessagePort* aEntangledPort);
+
+  virtual bool CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
 
-  virtual already_AddRefed<MessagePortBase>
-  Clone() override;
+  // These methods are useful for MessagePortChild
+
+  void Entangled(nsTArray<MessagePortMessage>& aMessages);
+  void MessagesReceived(nsTArray<MessagePortMessage>& aMessages);
+  void StopSendingDataConfirmed();
+  void Closed();
 
 private:
+  explicit MessagePort(nsPIDOMWindow* aWindow);
   ~MessagePort();
 
+  enum State {
+    // When a port is created by a MessageChannel it is entangled with the
+    // other. They both run on the same thread, same event loop and the
+    // messages are added to the queues without using PBackground actors.
+    // When one of the port is shipped, the state is changed to
+    // StateEntangling.
+    eStateUnshippedEntangled,
+
+    // If the port is closed or cloned when we are in this state, we set the
+    // mNextStep. This 'next' operation will be done when entangled() message
+    // is received.
+    eStateEntangling,
+
+    // When entangled() is received we send all the messages in the
+    // mMessagesForTheOtherPort to the actor and we change the state to
+    // StateEntangled. At this point the port is entangled with the other. We
+    // send and receive messages.
+    // If the port queue is not enabled, the received messages are stored in
+    // the mMessages.
+    eStateEntangled,
+
+    // When the port is cloned or disentangled we want to stop receiving
+    // messages. We call 'SendStopSendingData' to the actor and we wait for an
+    // answer. All the messages received between now and the
+    // 'StopSendingDataComfirmed are queued in the mMessages but not
+    // dispatched.
+    eStateDisentangling,
+
+    // When 'StopSendingDataConfirmed' is received, we can disentangle the port
+    // calling SendDisentangle in the actor because we are 100% sure that we
+    // don't receive any other message, so nothing will be lost.
+    // Disentangling the port we send all the messages from the mMessages
+    // though the actor.
+    eStateDisentangled
+  };
+
+  void Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
+                  uint32_t aSequenceID, bool mNeutered, State aState,
+                  ErrorResult& aRv);
+
+  void ConnectToPBackground();
+
   // Dispatch events from the Message Queue using a nsRunnable.
   void Dispatch();
 
+  void StartDisentangling();
+  void Disentangle();
+
+  void RemoveDocFromBFCache();
+
+  // This method is meant to keep alive the MessagePort when this object is
+  // creating the actor and until the actor is entangled.
+  // We release the object when the port is closed or disentangled.
+  void UpdateMustKeepAlive();
+
+  nsAutoPtr<workers::WorkerFeature> mWorkerFeature;
+
   nsRefPtr<DispatchEventRunnable> mDispatchRunnable;
 
-  nsRefPtr<MessagePort> mEntangledPort;
+  nsRefPtr<MessagePortChild> mActor;
+
+  nsRefPtr<MessagePort> mUnshippedEntangledPort;
+
+  nsTArray<nsRefPtr<SharedMessagePortMessage>> mMessages;
+  nsTArray<nsRefPtr<SharedMessagePortMessage>> mMessagesForTheOtherPort;
+
+  nsAutoPtr<MessagePortIdentifier> mIdentifier;
+
+  uint64_t mInnerID;
 
-  nsTArray<nsRefPtr<PostMessageRunnable> > mMessageQueue;
+  State mState;
+
+  // This 'nextStep' is used when we are waiting to be entangled but the
+  // content has called Clone() or Close().
+  enum {
+    eNextStepNone,
+    eNextStepDisentangle,
+    eNextStepClose
+  } mNextStep;
+
   bool mMessageQueueEnabled;
+
+  bool mIsKeptAlive;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MessagePort_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortChild.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "MessagePortChild.h"
+#include "MessagePort.h"
+#include "mozilla/dom/MessageEvent.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+
+namespace mozilla {
+namespace dom {
+
+bool
+MessagePortChild::RecvStopSendingDataConfirmed()
+{
+  MOZ_ASSERT(mPort);
+  mPort->StopSendingDataConfirmed();
+  MOZ_ASSERT(!mPort);
+  return true;
+}
+
+bool
+MessagePortChild::RecvEntangled(nsTArray<MessagePortMessage>&& aMessages)
+{
+  MOZ_ASSERT(mPort);
+  mPort->Entangled(aMessages);
+  return true;
+}
+
+bool
+MessagePortChild::RecvReceiveData(nsTArray<MessagePortMessage>&& aMessages)
+{
+  MOZ_ASSERT(mPort);
+  mPort->MessagesReceived(aMessages);
+  return true;
+}
+
+void
+MessagePortChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mPort) {
+    mPort->Closed();
+    MOZ_ASSERT(!mPort);
+  }
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortChild.h
@@ -0,0 +1,52 @@
+/* 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_MessagePortChild_h
+#define mozilla_dom_MessagePortChild_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/PMessagePortChild.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePort;
+
+class MessagePortChild final : public PMessagePortChild
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(MessagePortChild)
+
+  MessagePortChild() {}
+
+  void SetPort(MessagePort* aPort)
+  {
+    mPort = aPort;
+  }
+
+private:
+  ~MessagePortChild()
+  {
+    MOZ_ASSERT(!mPort);
+  }
+
+  virtual bool
+  RecvEntangled(nsTArray<MessagePortMessage>&& aMessages) override;
+
+  virtual bool
+  RecvReceiveData(nsTArray<MessagePortMessage>&& aMessages) override;
+
+  virtual bool RecvStopSendingDataConfirmed() override;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  // This is a raw pointer because this child is owned by this MessagePort.
+  MessagePort* mPort;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_MessagePortChild_h
rename from dom/base/MessagePortList.cpp
rename to dom/messagechannel/MessagePortList.cpp
rename from dom/base/MessagePortList.h
rename to dom/messagechannel/MessagePortList.h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortParent.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "MessagePortParent.h"
+#include "MessagePortService.h"
+#include "SharedMessagePortMessage.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+namespace dom {
+
+MessagePortParent::MessagePortParent(const nsID& aUUID)
+  : mService(MessagePortService::GetOrCreate())
+  , mUUID(aUUID)
+  , mEntangled(false)
+  , mCanSendData(true)
+{
+  MOZ_ASSERT(mService);
+}
+
+MessagePortParent::~MessagePortParent()
+{
+  MOZ_ASSERT(!mService);
+  MOZ_ASSERT(!mEntangled);
+}
+
+bool
+MessagePortParent::Entangle(const nsID& aDestinationUUID,
+                            const uint32_t& aSequenceID)
+{
+  if (!mService) {
+    NS_WARNING("Entangle is called after a shutdown!");
+    return false;
+  }
+
+  MOZ_ASSERT(!mEntangled);
+
+  return mService->RequestEntangling(this, aDestinationUUID, aSequenceID);
+}
+
+bool
+MessagePortParent::RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages)
+{
+  // This converts the object in a data struct where we have BlobImpls.
+  FallibleTArray<nsRefPtr<SharedMessagePortMessage>> messages;
+  if (NS_WARN_IF(
+      !SharedMessagePortMessage::FromMessagesToSharedParent(aMessages,
+                                                            messages))) {
+    return false;
+  }
+
+  if (!mEntangled) {
+    return false;
+  }
+
+  if (!mService) {
+    NS_WARNING("Entangle is called after a shutdown!");
+    return false;
+  }
+
+  if (messages.IsEmpty()) {
+    return false;
+  }
+
+  return mService->PostMessages(this, messages);
+}
+
+bool
+MessagePortParent::RecvDisentangle(nsTArray<MessagePortMessage>&& aMessages)
+{
+  // This converts the object in a data struct where we have BlobImpls.
+  FallibleTArray<nsRefPtr<SharedMessagePortMessage>> messages;
+  if (NS_WARN_IF(
+      !SharedMessagePortMessage::FromMessagesToSharedParent(aMessages,
+                                                            messages))) {
+    return false;
+  }
+
+  if (!mEntangled) {
+    return false;
+  }
+
+  if (!mService) {
+    NS_WARNING("Entangle is called after a shutdown!");
+    return false;
+  }
+
+  if (!mService->DisentanglePort(this, messages)) {
+    return false;
+  }
+
+  CloseAndDelete();
+  return true;
+}
+
+bool
+MessagePortParent::RecvStopSendingData()
+{
+  if (!mEntangled) {
+    return true;
+  }
+
+  mCanSendData = false;
+  unused << SendStopSendingDataConfirmed();
+  return true;
+}
+
+bool
+MessagePortParent::RecvClose()
+{
+  if (mService) {
+    MOZ_ASSERT(mEntangled);
+
+    if (!mService->ClosePort(this)) {
+      return false;
+    }
+
+    Close();
+  }
+
+  MOZ_ASSERT(!mEntangled);
+
+  unused << Send__delete__(this);
+  return true;
+}
+
+void
+MessagePortParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mService && mEntangled) {
+    // When the last parent is deleted, this service is freed but this cannot
+    // be done when the hashtables are written by CloseAll.
+    nsRefPtr<MessagePortService> kungFuDeathGrip = mService;
+    mService->ParentDestroy(this);
+  }
+}
+
+bool
+MessagePortParent::Entangled(const nsTArray<MessagePortMessage>& aMessages)
+{
+  MOZ_ASSERT(!mEntangled);
+  mEntangled = true;
+  return SendEntangled(aMessages);
+}
+
+void
+MessagePortParent::CloseAndDelete()
+{
+  Close();
+  unused << Send__delete__(this);
+}
+
+void
+MessagePortParent::Close()
+{
+  mService = nullptr;
+  mEntangled = false;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortParent.h
@@ -0,0 +1,61 @@
+/* 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_MessagePortParent_h
+#define mozilla_dom_MessagePortParent_h
+
+#include "mozilla/dom/PMessagePortParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortService;
+
+class MessagePortParent final : public PMessagePortParent
+{
+public:
+  explicit MessagePortParent(const nsID& aUUID);
+  ~MessagePortParent();
+
+  bool Entangle(const nsID& aDestinationUUID,
+                const uint32_t& aSequenceID);
+
+  bool Entangled(const nsTArray<MessagePortMessage>& aMessages);
+
+  void Close();
+  void CloseAndDelete();
+
+  bool CanSendData() const
+  {
+    return mCanSendData;
+  }
+
+  const nsID& ID() const
+  {
+    return mUUID;
+  }
+
+private:
+  virtual bool RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages)
+                                                                       override;
+
+  virtual bool RecvDisentangle(nsTArray<MessagePortMessage>&& aMessages)
+                                                                       override;
+
+  virtual bool RecvStopSendingData() override;
+
+  virtual bool RecvClose() override;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  nsRefPtr<MessagePortService> mService;
+  const nsID mUUID;
+  bool mEntangled;
+  bool mCanSendData;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_MessagePortParent_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortService.cpp
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "MessagePortService.h"
+#include "MessagePortParent.h"
+#include "SharedMessagePortMessage.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/unused.h"
+#include "nsDataHashtable.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+StaticRefPtr<MessagePortService> gInstance;
+} // anonymous namespace
+
+class MessagePortService::MessagePortServiceData final
+{
+public:
+  explicit MessagePortServiceData(const nsID& aDestinationUUID)
+    : mDestinationUUID(aDestinationUUID)
+    , mSequenceID(1)
+    , mParent(nullptr)
+  {
+    MOZ_COUNT_CTOR(MessagePortServiceData);
+  }
+
+  MessagePortServiceData(const MessagePortServiceData& aOther) = delete;
+  MessagePortServiceData& operator=(const MessagePortServiceData&) = delete;
+
+  ~MessagePortServiceData()
+  {
+    MOZ_COUNT_DTOR(MessagePortServiceData);
+  }
+
+  nsID mDestinationUUID;
+
+  uint32_t mSequenceID;
+  MessagePortParent* mParent;
+
+  struct NextParent
+  {
+    uint32_t mSequenceID;
+    // MessagePortParent keeps the service alive, and we don't want a cycle.
+    MessagePortParent* mParent;
+  };
+
+  FallibleTArray<NextParent> mNextParents;
+  FallibleTArray<nsRefPtr<SharedMessagePortMessage>> mMessages;
+};
+
+/* static */ MessagePortService*
+MessagePortService::GetOrCreate()
+{
+  if (!gInstance) {
+    gInstance = new MessagePortService();
+  }
+
+  return gInstance;
+}
+
+bool
+MessagePortService::RequestEntangling(MessagePortParent* aParent,
+                                      const nsID& aDestinationUUID,
+                                      const uint32_t& aSequenceID)
+{
+  MOZ_ASSERT(aParent);
+  MessagePortServiceData* data;
+
+  // If we don't have a MessagePortServiceData, we must create 2 of them for
+  // both ports.
+  if (!mPorts.Get(aParent->ID(), &data)) {
+    // Create the MessagePortServiceData for the destination.
+    if (mPorts.Get(aDestinationUUID, nullptr)) {
+      MOZ_ASSERT(false, "The creation of the 2 ports should be in sync.");
+      return false;
+    }
+
+    data = new MessagePortServiceData(aParent->ID());
+    mPorts.Put(aDestinationUUID, data);
+
+    data = new MessagePortServiceData(aDestinationUUID);
+    mPorts.Put(aParent->ID(), data);
+  }
+
+  // This is a security check.
+  if (!data->mDestinationUUID.Equals(aDestinationUUID)) {
+    MOZ_ASSERT(false, "DestinationUUIDs do not match!");
+    return false;
+  }
+
+  if (aSequenceID < data->mSequenceID) {
+    MOZ_ASSERT(false, "Invalid sequence ID!");
+    return false;
+  }
+
+  if (aSequenceID == data->mSequenceID) {
+    if (data->mParent) {
+      MOZ_ASSERT(false, "Two ports cannot have the same sequenceID.");
+      return false;
+    }
+
+    // We activate this port, sending all the messages.
+    data->mParent = aParent;
+    FallibleTArray<MessagePortMessage> array;
+    if (!SharedMessagePortMessage::FromSharedToMessagesParent(aParent,
+                                                              data->mMessages,
+                                                              array)) {
+      return false;
+    }
+
+    data->mMessages.Clear();
+    return aParent->Entangled(array);
+  }
+
+  // This new parent will be the next one when a Disentangle request is
+  // received from the current parent.
+  MessagePortServiceData::NextParent* nextParent =
+    data->mNextParents.AppendElement(mozilla::fallible);
+  if (!nextParent) {
+    return false;
+  }
+
+  nextParent->mSequenceID = aSequenceID;
+  nextParent->mParent = aParent;
+
+  return true;
+}
+
+bool
+MessagePortService::DisentanglePort(
+                  MessagePortParent* aParent,
+                  FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages)
+{
+  MessagePortServiceData* data;
+  if (!mPorts.Get(aParent->ID(), &data)) {
+    MOZ_ASSERT(false, "Unknown MessagePortParent should not happen.");
+    return false;
+  }
+
+  if (data->mParent != aParent) {
+    MOZ_ASSERT(false, "DisentanglePort() should be called just from the correct parent.");
+    return false;
+  }
+
+  // Let's put the messages in the correct order. |aMessages| contains the
+  // unsent messages so they have to go first.
+  if (!aMessages.AppendElements(data->mMessages, mozilla::fallible)) {
+    return false;
+  }
+
+  data->mMessages.Clear();
+
+  ++data->mSequenceID;
+
+  // If we don't have a parent, we have to store the pending messages and wait.
+  uint32_t index = 0;
+  MessagePortParent* nextParent = nullptr;
+  for (; index < data->mNextParents.Length(); ++index) {
+    if (data->mNextParents[index].mSequenceID == data->mSequenceID) {
+      nextParent = data->mNextParents[index].mParent;
+      break;
+    }
+  }
+
+  // We didn't find the parent.
+  if (!nextParent) {
+    data->mMessages.SwapElements(aMessages);
+    data->mParent = nullptr;
+    return true;
+  }
+
+  data->mParent = nextParent;
+  data->mNextParents.RemoveElementAt(index);
+
+  FallibleTArray<MessagePortMessage> array;
+  if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
+                                                            aMessages,
+                                                            array)) {
+    return false;
+  }
+
+  unused << data->mParent->Entangled(array);
+  return true;
+}
+
+bool
+MessagePortService::ClosePort(MessagePortParent* aParent)
+{
+  MessagePortServiceData* data;
+  if (!mPorts.Get(aParent->ID(), &data)) {
+    MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
+    return false;
+  }
+
+  if (data->mParent != aParent) {
+    MOZ_ASSERT(false, "ClosePort() should be called just from the correct parent.");
+    return false;
+  }
+
+  if (!data->mNextParents.IsEmpty()) {
+    MOZ_ASSERT(false, "ClosePort() should be called when there are not next parents.");
+    return false;
+  }
+
+  // We don't want to send a message to this parent.
+  data->mParent = nullptr;
+
+  CloseAll(aParent->ID());
+  return true;
+}
+
+#ifdef DEBUG
+PLDHashOperator
+MessagePortService::CloseAllDebugCheck(const nsID& aID,
+                                       MessagePortServiceData* aData,
+                                       void* aPtr)
+{
+  nsID* id = static_cast<nsID*>(aPtr);
+  MOZ_ASSERT(!id->Equals(aID));
+  return PL_DHASH_NEXT;
+}
+#endif
+
+void
+MessagePortService::CloseAll(const nsID& aUUID)
+{
+  MessagePortServiceData* data;
+  if (!mPorts.Get(aUUID, &data)) {
+    MaybeShutdown();
+    return;
+  }
+
+  if (data->mParent) {
+    data->mParent->Close();
+  }
+
+  for (const MessagePortServiceData::NextParent& parent : data->mNextParents) {
+    parent.mParent->CloseAndDelete();
+  }
+
+  nsID destinationUUID = data->mDestinationUUID;
+  mPorts.Remove(aUUID);
+
+  CloseAll(destinationUUID);
+
+#ifdef DEBUG
+  mPorts.EnumerateRead(CloseAllDebugCheck, const_cast<nsID*>(&aUUID));
+#endif
+
+  MaybeShutdown();
+}
+
+// This service can be dismissed when there are not active ports.
+void
+MessagePortService::MaybeShutdown()
+{
+  if (mPorts.Count() == 0) {
+    gInstance = nullptr;
+  }
+}
+
+bool
+MessagePortService::PostMessages(
+                  MessagePortParent* aParent,
+                  FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages)
+{
+  MessagePortServiceData* data;
+  if (!mPorts.Get(aParent->ID(), &data)) {
+    MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
+    return false;
+  }
+
+  if (data->mParent != aParent) {
+    MOZ_ASSERT(false, "PostMessages() should be called just from the correct parent.");
+    return false;
+  }
+
+  MOZ_ALWAYS_TRUE(mPorts.Get(data->mDestinationUUID, &data));
+
+  if (!data->mMessages.AppendElements(aMessages, mozilla::fallible)) {
+    return false;
+  }
+
+  // If the parent can send data to the child, let's proceed.
+  if (data->mParent && data->mParent->CanSendData()) {
+    FallibleTArray<MessagePortMessage> messages;
+    if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
+                                                              data->mMessages,
+                                                              messages)) {
+      return false;
+    }
+
+    data->mMessages.Clear();
+    unused << data->mParent->SendReceiveData(messages);
+  }
+
+  return true;
+}
+
+void
+MessagePortService::ParentDestroy(MessagePortParent* aParent)
+{
+  // This port has already been destroyed.
+  MessagePortServiceData* data;
+  if (!mPorts.Get(aParent->ID(), &data)) {
+    return;
+  }
+
+  if (data->mParent != aParent) {
+    // We don't want to send a message to this parent.
+    for (uint32_t i = 0; i < data->mNextParents.Length(); ++i) {
+      if (aParent == data->mNextParents[i].mParent) {
+       data->mNextParents.RemoveElementAt(i);
+       break;
+      }
+    }
+  }
+
+  CloseAll(aParent->ID());
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortService.h
@@ -0,0 +1,61 @@
+/* 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_MessagePortService_h
+#define mozilla_dom_MessagePortService_h
+
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortParent;
+class SharedMessagePortMessage;
+
+class MessagePortService final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(MessagePortService)
+
+  static MessagePortService* GetOrCreate();
+
+  bool RequestEntangling(MessagePortParent* aParent,
+                         const nsID& aDestinationUUID,
+                         const uint32_t& aSequenceID);
+
+  bool DisentanglePort(
+                 MessagePortParent* aParent,
+                 FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages);
+
+  bool ClosePort(MessagePortParent* aParent);
+
+  bool PostMessages(
+                 MessagePortParent* aParent,
+                 FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages);
+
+  void ParentDestroy(MessagePortParent* aParent);
+
+private:
+  ~MessagePortService() {}
+
+  void CloseAll(const nsID& aUUID);
+  void MaybeShutdown();
+
+  class MessagePortServiceData;
+
+#ifdef DEBUG
+  static PLDHashOperator
+  CloseAllDebugCheck(const nsID& aID, MessagePortServiceData* aData,
+                     void* aPtr);
+#endif
+
+  nsClassHashtable<nsIDHashKey, MessagePortServiceData> mPorts;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_MessagePortService_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortUtils.cpp
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "MessagePortUtils.h"
+#include "MessagePort.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+
+namespace mozilla {
+namespace dom {
+namespace messageport {
+
+namespace {
+
+struct MOZ_STACK_CLASS StructuredCloneClosureInternal
+{
+  StructuredCloneClosureInternal(
+    StructuredCloneClosure& aClosure, nsPIDOMWindow* aWindow)
+    : mClosure(aClosure)
+    , mWindow(aWindow)
+  { }
+
+  StructuredCloneClosure& mClosure;
+  nsPIDOMWindow* mWindow;
+  nsTArray<nsRefPtr<MessagePort>> mMessagePorts;
+  nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+};
+
+struct MOZ_STACK_CLASS StructuredCloneClosureInternalReadOnly
+{
+  StructuredCloneClosureInternalReadOnly(
+    const StructuredCloneClosure& aClosure, nsPIDOMWindow* aWindow)
+    : mClosure(aClosure)
+    , mWindow(aWindow)
+  { }
+
+  const StructuredCloneClosure& mClosure;
+  nsPIDOMWindow* mWindow;
+  nsTArray<nsRefPtr<MessagePort>> mMessagePorts;
+  nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+};
+
+void
+Error(JSContext* aCx, uint32_t aErrorId)
+{
+  if (NS_IsMainThread()) {
+    NS_DOMStructuredCloneError(aCx, aErrorId);
+  } else {
+    Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+  }
+}
+
+JSObject*
+Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
+     uint32_t aData, void* aClosure)
+{
+  MOZ_ASSERT(aClosure);
+
+  auto* closure = static_cast<StructuredCloneClosureInternalReadOnly*>(aClosure);
+
+  if (aTag == SCTAG_DOM_BLOB) {
+    // 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);
+    {
+      MOZ_ASSERT(aData < closure->mClosure.mBlobImpls.Length());
+      nsRefPtr<BlobImpl> blobImpl = closure->mClosure.mBlobImpls[aData];
+
+#ifdef DEBUG
+      {
+        // Blob should not be mutable.
+        bool isMutable;
+        MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+        MOZ_ASSERT(!isMutable);
+      }
+#endif
+
+      // Let's create a new blob with the correct parent.
+      nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+      MOZ_ASSERT(global);
+
+      nsRefPtr<Blob> newBlob = Blob::Create(global, blobImpl);
+      if (!ToJSValue(aCx, newBlob, &val)) {
+        return nullptr;
+      }
+    }
+
+    return &val.toObject();
+  }
+
+  return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
+}
+
+bool
+Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+      JS::Handle<JSObject*> aObj, void* aClosure)
+{
+  MOZ_ASSERT(aClosure);
+
+  auto* closure = static_cast<StructuredCloneClosureInternal*>(aClosure);
+
+  // See if the wrapped native is a File/Blob.
+  {
+    Blob* blob = nullptr;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
+        NS_SUCCEEDED(blob->SetMutable(false)) &&
+        JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
+                           closure->mClosure.mBlobImpls.Length())) {
+      closure->mClosure.mBlobImpls.AppendElement(blob->Impl());
+      return true;
+    }
+  }
+
+  return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
+}
+
+bool
+ReadTransfer(JSContext* aCx, JSStructuredCloneReader* aReader,
+             uint32_t aTag, void* aContent, uint64_t aExtraData,
+             void* aClosure, JS::MutableHandle<JSObject*> aReturnObject)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aClosure);
+
+  auto* closure = static_cast<StructuredCloneClosureInternalReadOnly*>(aClosure);
+
+  if (aTag != SCTAG_DOM_MAP_MESSAGEPORT) {
+    return false;
+  }
+
+  MOZ_ASSERT(aContent == 0);
+  MOZ_ASSERT(aExtraData < closure->mClosure.mMessagePortIdentifiers.Length());
+
+  ErrorResult rv;
+  nsRefPtr<MessagePort> port =
+    MessagePort::Create(closure->mWindow,
+                        closure->mClosure.mMessagePortIdentifiers[(uint32_t)aExtraData],
+                        rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return false;
+  }
+
+  closure->mMessagePorts.AppendElement(port);
+
+  JS::Rooted<JS::Value> value(aCx);
+  if (!GetOrCreateDOMReflector(aCx, port, &value)) {
+    JS_ClearPendingException(aCx);
+    return false;
+  }
+
+  aReturnObject.set(&value.toObject());
+  return true;
+}
+
+bool
+WriteTransfer(JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
+              uint32_t* aTag, JS::TransferableOwnership* aOwnership,
+              void** aContent, uint64_t* aExtraData)
+{
+  MOZ_ASSERT(aClosure);
+
+  auto* closure = static_cast<StructuredCloneClosureInternal*>(aClosure);
+
+  MessagePortBase* port = nullptr;
+  nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  if (closure->mTransferredPorts.Contains(port)) {
+    // No duplicates.
+    return false;
+  }
+
+  MessagePortIdentifier identifier;
+  if (!port->CloneAndDisentangle(identifier)) {
+    return false;
+  }
+
+  closure->mClosure.mMessagePortIdentifiers.AppendElement(identifier);
+  closure->mTransferredPorts.AppendElement(port);
+
+  *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
+  *aOwnership = JS::SCTAG_TMO_CUSTOM;
+  *aContent = nullptr;
+  *aExtraData = closure->mClosure.mMessagePortIdentifiers.Length() - 1;
+
+  return true;
+}
+
+const JSStructuredCloneCallbacks gCallbacks = {
+  Read,
+  Write,
+  Error,
+  ReadTransfer,
+  WriteTransfer,
+  nullptr
+};
+
+} // anonymous namespace
+
+bool
+ReadStructuredCloneWithTransfer(JSContext* aCx, nsTArray<uint8_t>& aData,
+                                const StructuredCloneClosure& aClosure,
+                                JS::MutableHandle<JS::Value> aClone,
+                                nsPIDOMWindow* aParentWindow,
+                                nsTArray<nsRefPtr<MessagePort>>& aMessagePorts)
+{
+  auto* data = reinterpret_cast<uint64_t*>(aData.Elements());
+  size_t dataLen = aData.Length();
+  MOZ_ASSERT(!(dataLen % sizeof(*data)));
+
+  StructuredCloneClosureInternalReadOnly internalClosure(aClosure,
+                                                         aParentWindow);
+
+  bool rv = JS_ReadStructuredClone(aCx, data, dataLen,
+                                   JS_STRUCTURED_CLONE_VERSION, aClone,
+                                   &gCallbacks, &internalClosure);
+  if (rv) {
+    aMessagePorts.SwapElements(internalClosure.mMessagePorts);
+  }
+
+  return rv;
+}
+
+bool
+WriteStructuredCloneWithTransfer(JSContext* aCx, JS::Handle<JS::Value> aSource,
+                                 JS::Handle<JS::Value> aTransferable,
+                                 nsTArray<uint8_t>& aData,
+                                 StructuredCloneClosure& aClosure)
+{
+  StructuredCloneClosureInternal internalClosure(aClosure, nullptr);
+  JSAutoStructuredCloneBuffer buffer(&gCallbacks, &internalClosure);
+
+  if (!buffer.write(aCx, aSource, aTransferable, &gCallbacks,
+                    &internalClosure)) {
+    return false;
+  }
+
+  FallibleTArray<uint8_t> cloneData;
+  if (NS_WARN_IF(!cloneData.SetLength(buffer.nbytes(), mozilla::fallible))) {
+    return false;
+  }
+
+  uint64_t* data;
+  size_t size;
+  buffer.steal(&data, &size);
+
+  memcpy(cloneData.Elements(), data, size);
+  js_free(data);
+
+  MOZ_ASSERT(aData.IsEmpty());
+  aData.SwapElements(cloneData);
+  return true;
+}
+
+void
+FreeStructuredClone(nsTArray<uint8_t>& aData, StructuredCloneClosure& aClosure)
+{
+  auto* data = reinterpret_cast<uint64_t*>(aData.Elements());
+  size_t dataLen = aData.Length();
+  MOZ_ASSERT(!(dataLen % sizeof(*data)));
+
+  JS_ClearStructuredClone(data, dataLen, &gCallbacks, &aClosure, false);
+  aData.Clear();
+}
+
+} // messageport namespace
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortUtils.h
@@ -0,0 +1,55 @@
+/* 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_MessagePortUtils_h
+#define mozilla_dom_MessagePortUtils_h
+
+#include "MessagePort.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/PMessagePort.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+namespace messageport {
+
+struct
+StructuredCloneClosure
+{
+  nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
+  nsTArray<MessagePortIdentifier> mMessagePortIdentifiers;
+};
+
+struct
+StructuredCloneData
+{
+  StructuredCloneData() : mData(nullptr), mDataLength(0) {}
+  uint64_t* mData;
+  size_t mDataLength;
+  StructuredCloneClosure mClosure;
+};
+
+bool
+ReadStructuredCloneWithTransfer(JSContext* aCx, nsTArray<uint8_t>& aData,
+                                const StructuredCloneClosure& aClosure,
+                                JS::MutableHandle<JS::Value> aClone,
+                                nsPIDOMWindow* aParentWindow,
+                                nsTArray<nsRefPtr<MessagePort>>& aMessagePorts);
+
+bool
+WriteStructuredCloneWithTransfer(JSContext* aCx, JS::Handle<JS::Value> aSource,
+                                 JS::Handle<JS::Value> aTransferable,
+                                 nsTArray<uint8_t>& aData,
+                                 StructuredCloneClosure& aClosure);
+
+void
+FreeStructuredClone(nsTArray<uint8_t>& aData,
+                    StructuredCloneClosure& aClosure);
+
+} // messageport namespace
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_MessagePortUtils_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/PMessagePort.ipdl
@@ -0,0 +1,62 @@
+/* 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 protocol PBackground;
+include protocol PBlob;
+
+using struct nsID from "nsID.h";
+
+namespace mozilla {
+namespace dom {
+
+struct MessagePortIdentifier
+{
+  nsID uuid;
+  nsID destinationUuid;
+  uint32_t sequenceId;
+  bool neutered;
+};
+
+struct MessagePortMessage
+{
+  MessagePortIdentifier[] transferredPorts;
+  uint8_t[] data;
+  PBlob[] blobs;
+};
+
+// This protocol is used for the MessageChannel/MessagePort API
+protocol PMessagePort
+{
+  manager PBackground;
+
+  /* Many of these methods are used just for the shutdown sequence. The
+     correct sequence for the child actor is:
+     1. SendStopSendingData();
+     2. RecvStopSendingDataConfirmed();
+     3. SendClose();
+     4. Recv__delete__(); */
+
+  /* When the port is transferred the sequence is:
+     1. SendStopSendingData();
+     2. RecvStopSendingDataConfirmed();
+     3. SendDisentangle();
+     4. Recv__delete__(); */
+
+parent:
+  PostMessages(MessagePortMessage[] messages);
+  Disentangle(MessagePortMessage[] messages);
+  StopSendingData();
+  Close();
+
+child:
+  Entangled(MessagePortMessage[] messages);
+  ReceiveData(MessagePortMessage[] messages);
+  StopSendingDataConfirmed();
+
+  __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/SharedMessagePortMessage.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "SharedMessagePortMessage.h"
+#include "MessagePort.h"
+#include "MessagePortChild.h"
+#include "MessagePortParent.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/PMessagePort.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+SharedMessagePortMessage::~SharedMessagePortMessage()
+{
+  if (!mData.IsEmpty()) {
+    FreeStructuredClone(mData, mClosure);
+  }
+}
+
+/* static */ void
+SharedMessagePortMessage::FromSharedToMessagesChild(
+                      MessagePortChild* aActor,
+                      const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
+                      nsTArray<MessagePortMessage>& aArray)
+{
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aArray.IsEmpty());
+  aArray.SetCapacity(aData.Length());
+
+  PBackgroundChild* backgroundManager = aActor->Manager();
+  MOZ_ASSERT(backgroundManager);
+
+  for (auto& data : aData) {
+    MessagePortMessage* message = aArray.AppendElement();
+    message->data().SwapElements(data->mData);
+
+    const nsTArray<nsRefPtr<BlobImpl>>& blobImpls =
+      data->mClosure.mBlobImpls;
+    if (!blobImpls.IsEmpty()) {
+      message->blobsChild().SetCapacity(blobImpls.Length());
+
+      for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
+        PBlobChild* blobChild =
+          BackgroundChild::GetOrCreateActorForBlobImpl(backgroundManager,
+                                                       blobImpls[i]);
+        message->blobsChild().AppendElement(blobChild);
+      }
+    }
+
+    message->transferredPorts().AppendElements(
+      data->mClosure.mMessagePortIdentifiers);
+  }
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromMessagesToSharedChild(
+                      nsTArray<MessagePortMessage>& aArray,
+                      FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData)
+{
+  MOZ_ASSERT(aData.IsEmpty());
+
+  if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
+    return false;
+  }
+
+  for (auto& message : aArray) {
+    nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+    data->mData.SwapElements(message.data());
+
+    const nsTArray<PBlobChild*>& blobs = message.blobsChild();
+    if (!blobs.IsEmpty()) {
+      data->mClosure.mBlobImpls.SetCapacity(blobs.Length());
+
+      for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
+        nsRefPtr<BlobImpl> impl =
+          static_cast<BlobChild*>(blobs[i])->GetBlobImpl();
+        data->mClosure.mBlobImpls.AppendElement(impl);
+      }
+    }
+
+    data->mClosure.mMessagePortIdentifiers.AppendElements(
+      message.transferredPorts());
+
+    if (!aData.AppendElement(data, mozilla::fallible)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromSharedToMessagesParent(
+                      MessagePortParent* aActor,
+                      const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
+                      FallibleTArray<MessagePortMessage>& aArray)
+{
+  MOZ_ASSERT(aArray.IsEmpty());
+
+  if (NS_WARN_IF(!aArray.SetCapacity(aData.Length(), mozilla::fallible))) {
+    return false;
+  }
+
+  PBackgroundParent* backgroundManager = aActor->Manager();
+  MOZ_ASSERT(backgroundManager);
+
+  for (auto& data : aData) {
+    MessagePortMessage* message = aArray.AppendElement(mozilla::fallible);
+    message->data().SwapElements(data->mData);
+
+    const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->mClosure.mBlobImpls;
+    if (!blobImpls.IsEmpty()) {
+      message->blobsParent().SetCapacity(blobImpls.Length());
+
+      for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
+        PBlobParent* blobParent =
+          BackgroundParent::GetOrCreateActorForBlobImpl(backgroundManager,
+                                                        blobImpls[i]);
+        message->blobsParent().AppendElement(blobParent);
+      }
+    }
+
+    message->transferredPorts().AppendElements(
+      data->mClosure.mMessagePortIdentifiers);
+  }
+
+  return true;
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromMessagesToSharedParent(
+                      nsTArray<MessagePortMessage>& aArray,
+                      FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData)
+{
+  MOZ_ASSERT(aData.IsEmpty());
+
+  if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
+    return false;
+  }
+
+  for (auto& message : aArray) {
+    nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+    data->mData.SwapElements(message.data());
+
+    const nsTArray<PBlobParent*>& blobs = message.blobsParent();
+    if (!blobs.IsEmpty()) {
+      data->mClosure.mBlobImpls.SetCapacity(blobs.Length());
+
+      for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
+        nsRefPtr<BlobImpl> impl =
+          static_cast<BlobParent*>(blobs[i])->GetBlobImpl();
+        data->mClosure.mBlobImpls.AppendElement(impl);
+      }
+    }
+
+    data->mClosure.mMessagePortIdentifiers.AppendElements(
+      message.transferredPorts());
+
+    if (!aData.AppendElement(data, mozilla::fallible)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/SharedMessagePortMessage.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_SharedMessagePortMessage_h
+#define mozilla_dom_SharedMessagePortMessage_h
+
+#include "MessagePortUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortChild;
+class MessagePortMessage;
+class MessagePortParent;
+
+class SharedMessagePortMessage final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(SharedMessagePortMessage)
+
+  nsTArray<uint8_t> mData;
+  messageport::StructuredCloneClosure mClosure;
+
+  SharedMessagePortMessage()
+  {}
+
+  static void
+  FromSharedToMessagesChild(
+                      MessagePortChild* aActor,
+                      const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
+                      nsTArray<MessagePortMessage>& aArray);
+
+  static bool
+  FromMessagesToSharedChild(
+                     nsTArray<MessagePortMessage>& aArray,
+                     FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData);
+
+  static bool
+  FromSharedToMessagesParent(
+                      MessagePortParent* aActor,
+                      const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
+                      FallibleTArray<MessagePortMessage>& aArray);
+
+  static bool
+  FromMessagesToSharedParent(
+                     nsTArray<MessagePortMessage>& aArray,
+                     FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData);
+
+private:
+  ~SharedMessagePortMessage();
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_SharedMessagePortMessage_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/moz.build
@@ -0,0 +1,41 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+TEST_DIRS += ['tests']
+
+EXPORTS.mozilla.dom += [
+    'MessageChannel.h',
+    'MessagePort.h',
+    'MessagePortChild.h',
+    'MessagePortList.h',
+    'MessagePortParent.h',
+]
+
+UNIFIED_SOURCES += [
+    'MessageChannel.cpp',
+    'MessagePort.cpp',
+    'MessagePortChild.cpp',
+    'MessagePortList.cpp',
+    'MessagePortParent.cpp',
+    'MessagePortService.cpp',
+    'MessagePortUtils.cpp',
+    'SharedMessagePortMessage.cpp',
+]
+
+IPDL_SOURCES += [
+    'PMessagePort.ipdl',
+]
+
+LOCAL_INCLUDES += [
+    '../base',
+    '../events',
+    '../workers',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+FAIL_ON_WARNINGS = True
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/chrome.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+support-files =
+  iframe_messageChannel_chrome.html
+
+[test_messageChannel.xul]
rename from dom/base/test/iframe_messageChannel_chrome.html
rename to dom/messagechannel/tests/iframe_messageChannel_chrome.html
rename from dom/base/test/iframe_messageChannel_cloning.html
rename to dom/messagechannel/tests/iframe_messageChannel_cloning.html
rename from dom/base/test/iframe_messageChannel_pingpong.html
rename to dom/messagechannel/tests/iframe_messageChannel_pingpong.html
rename from dom/base/test/iframe_messageChannel_post.html
rename to dom/messagechannel/tests/iframe_messageChannel_post.html
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/iframe_messageChannel_sharedWorker2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+  <script type="application/javascript">
+
+  var a = new SharedWorker('sharedWorker2_messageChannel.js');
+  a.port.onmessage = function(evt) {
+    evt.ports[0].postMessage("Hello from the iframe!");
+  }
+
+  </script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/iframe_messageChannel_transferable.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+  <script type="application/javascript">
+
+  function ok(what, msg) {
+    window.parent.postMessage({type: what ? 'OK' : 'KO', msg: msg }, '*');
+  }
+
+  window.addEventListener('message', receiveMessage, false);
+  function receiveMessage(evt) {
+    ok(evt.ports.length == 1, "Port transferred!");
+
+    var a = new MessageChannel();
+    ok(a, "MessageChannel created");
+    evt.ports[0].postMessage('hello world!', [a.port2]);
+    a.port1.onmessage = function(evt) {
+      evt.target.postMessage(evt.data);
+    }
+  }
+
+  </script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/mochitest.ini
@@ -0,0 +1,25 @@
+[DEFAULT]
+support-files =
+  iframe_messageChannel_cloning.html
+  iframe_messageChannel_pingpong.html
+  iframe_messageChannel_post.html
+  iframe_messageChannel_transferable.html
+  worker_messageChannel.js
+  worker_messageChannel_any.js
+  sharedWorker_messageChannel.js
+  sharedWorker2_messageChannel.js
+  iframe_messageChannel_sharedWorker2.html
+
+[test_messageChannel.html]
+[test_messageChannel_cloning.html]
+[test_messageChannel_pingpong.html]
+[test_messageChannel_post.html]
+[test_messageChannel_pref.html]
+[test_messageChannel_start.html]
+[test_messageChannel_transferable.html]
+[test_messageChannel_unshipped.html]
+[test_messageChannel_worker.html]
+[test_messageChannel_selfTransferring.html]
+[test_messageChannel_sharedWorker.html]
+[test_messageChannel_sharedWorker2.html]
+[test_messageChannel_any.html]
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MOCHITEST_MANIFESTS += ['mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/sharedWorker2_messageChannel.js
@@ -0,0 +1,7 @@
+var mc = new MessageChannel();
+var i = 0;
+
+onconnect = function(evt) {
+  dump("CONNECTING: "+ i +"\n");
+  evt.ports[0].postMessage(42, [mc['port' + ++i]]);
+}
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/sharedWorker_messageChannel.js
@@ -0,0 +1,8 @@
+onconnect = function(evt) {
+  var mc = new MessageChannel();
+
+  evt.ports[0].postMessage(42, [mc.port2]);
+  mc.port1.onmessage = function(e) {
+    mc.port1.postMessage(e.data);
+  }
+}
rename from dom/base/test/test_messageChannel.html
rename to dom/messagechannel/tests/test_messageChannel.html
rename from dom/base/test/test_messageChannel.xul
rename to dom/messagechannel/tests/test_messageChannel.xul
--- a/dom/base/test/test_messageChannel.xul
+++ b/dom/messagechannel/tests/test_messageChannel.xul
@@ -18,17 +18,17 @@
   ok(channel, "MessageChannel is created");
 
   channel.port1.onmessage = function(evt) {
     ok(true, "message received!");
     SimpleTest.finish();
   }
 
   var ifr = document.createElement('browser');
-  ifr.setAttribute("src", "http://mochi.test:8888/tests/dom/base/test/iframe_messageChannel_chrome.html");
+  ifr.setAttribute("src", "iframe_messageChannel_chrome.html");
   ifr.setAttribute("flex", "1");
   ifr.addEventListener('load', function() {
     ifr.contentWindow.postMessage(channel.port2, '*', [channel.port2]);
   });
 
   var body = document.getElementById("body");
   body.appendChild(ifr);
 
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_any.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+  <meta charset="utf-8">
+  <title>MessagePort/Channel any content</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+  <script type="application/javascript">
+
+var tests = [
+ 'hello world',
+ 123,
+ null,
+ true,
+ new Date(),
+ [ 1, 'test', true, new Date() ],
+ { a: true, b:  null, c: new Date(), d: [ true, false, {} ] },
+ new Blob([123], { type: 'plain/text' })
+];
+
+var currentTest = null;
+
+function getType(a) {
+  if (a === null || a === undefined)
+    return 'null';
+
+  if (Array.isArray(a))
+    return 'array';
+
+  if (typeof a == 'object')
+    return 'object';
+
+  return 'primitive';
+}
+
+function compare(a, b) {
+  is (getType(a), getType(b), 'Type matches');
+
+  var type = getType(a);
+  if (type == 'array') {
+    is (a.length, b.length, 'Array.length matches');
+    for (var i = 0; i < a.length; ++i) {
+      compare(a[i], b[i]);
+    }
+
+    return;
+  }
+
+  if (type == 'object') {
+    ok (a !== b, 'They should not match');
+
+    var aProps = [];
+    for (var p in a) aProps.push(p);
+
+    var bProps = [];
+    for (var p in b) bProps.push(p);
+
+    is (aProps.length, bProps.length, 'Props match');
+    is (aProps.sort().toSource(), bProps.sort().toSource(), 'Props match - using toSource()');
+
+    for (var p in a) {
+      compare(a[p], b[p]);
+    }
+
+    return;
+  }
+
+  if (type != 'null') {
+    is (a.toSource(), b.toSource(), 'Matching using toSource()');
+  }
+}
+
+function runTest() {
+  var mc = new MessageChannel('foobar');
+  ok(mc, "MessageChannel can be created");
+
+  mc.port1.onmessage = function(event) {
+    compare(event.data, currentTest);
+    next();
+  }
+
+  function next() {
+    if (!tests.length) {
+      SimpleTest.finish();
+      return;
+    }
+
+    currentTest = tests.shift();
+    mc.port1.postMessage(currentTest);
+  }
+
+  var worker = new Worker("worker_messageChannel_any.js");
+  worker.onmessage = function(event) {
+    if (event.data == "READY") {
+      next();
+    }
+  };
+
+  worker.postMessage(mc.port2, [mc.port2]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+  </script>
+</body>
+</html>
rename from dom/base/test/test_messageChannel_cloning.html
rename to dom/messagechannel/tests/test_messageChannel_cloning.html
rename from dom/base/test/test_messageChannel_pingpong.html
rename to dom/messagechannel/tests/test_messageChannel_pingpong.html
rename from dom/base/test/test_messageChannel_post.html
rename to dom/messagechannel/tests/test_messageChannel_post.html
rename from dom/base/test/test_messageChannel_pref.html
rename to dom/messagechannel/tests/test_messageChannel_pref.html
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_selfTransferring.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+  <meta charset="utf-8">
+  <title>MessagePort/Channel no self tranferring</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+  <script type="application/javascript">
+
+function runTest() {
+  var a = new MessageChannel();
+
+  var status = false;
+  try {
+    a.port1.postMessage('foobar', [a.port1]);
+  } catch(e) {
+    status =true;
+  }
+
+  ok(status, "Transfering the same port should throw");
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+  </script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_sharedWorker.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 677638 - sharedWorker</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <iframe name="x" id="x"></iframe>
+  <iframe name="y" id="y"></iframe>
+</div>
+<pre id="test">
+</pre>
+  <script type="application/javascript">
+
+  function runTest() {
+    var a = new SharedWorker('sharedWorker_messageChannel.js');
+    a.port.onmessage = function(evt) {
+      is(evt.ports.length, 1, "We received a port.");
+      evt.ports[0].onmessage = function(e) {
+        is(e.data, 42, "Message reiceved back!");
+        SimpleTest.finish();
+      }
+      evt.ports[0].postMessage(42);
+    }
+  }
+
+  SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+  SimpleTest.waitForExplicitFinish();
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_sharedWorker2.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 677638 - sharedWorker</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+  <div id="content"></div>
+
+  <script type="application/javascript">
+
+  function runTest() {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('src', "iframe_messageChannel_sharedWorker2.html");
+    document.getElementById('content').appendChild(iframe);
+
+    var a = new SharedWorker('sharedWorker2_messageChannel.js');
+    a.port.onmessage = function(evt) {
+      is(evt.ports.length, 1, "We received a port.");
+      evt.ports[0].onmessage = function(e) {
+        is(e.data, "Hello from the iframe!", "Message reiceved from the iframe!");
+        SimpleTest.finish();
+      }
+    }
+  }
+
+  SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+  SimpleTest.waitForExplicitFinish();
+  </script>
+</body>
+</html>
rename from dom/base/test/test_messageChannel_start.html
rename to dom/messagechannel/tests/test_messageChannel_start.html
rename from dom/base/test/test_messageChannel_transferable.html
rename to dom/messagechannel/tests/test_messageChannel_transferable.html
--- a/dom/base/test/test_messageChannel_transferable.html
+++ b/dom/messagechannel/tests/test_messageChannel_transferable.html
@@ -11,24 +11,25 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
 <div id="content"></div>
 <pre id="test">
 </pre>
   <script type="application/javascript">
 
-  function start() {
+  function basic_test() {
     var a = new MessageChannel();
     ok(a, "MessageChannel created");
 
     window.addEventListener('message', receiveMessage, false);
     function receiveMessage(evt) {
       if (evt.data.status == 'READY') {
-        runTest();
+        a.port1.postMessage({ab: ab, cb: ab}, [ab]);
+        ok(ab.byteLength == 0, "PostMessage - The size is: 0 == " + ab.byteLength)
       } else {
         ok(false, "Unknown message");
       }
     }
 
     var div = document.getElementById("content");
     ok(div, "Parent exists");
 
@@ -39,29 +40,72 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     function iframeLoaded() {
       ifr.contentWindow.postMessage({ port: a.port2 }, '*', [a.port2]);
     }
 
     a.port1.addEventListener('message', receivePortMessage, false);
     function receivePortMessage(evt) {
       is(evt.data.ab.byteLength, size, "The size is: " + size + " == " + ab.byteLength);
-      SimpleTest.finish();
+      window.removeEventListener('message', receiveMessage);
+      runTests();
     }
 
     // Start() is not implicity invoked when addEventListener is used.
     a.port1.start();
 
     var size = 1024 * 1024 * 32;
     var ab = new ArrayBuffer(size);
     is(ab.byteLength, size, "The size is: " + size + " == " + ab.byteLength);
+  }
 
-    function runTest() {
-      a.port1.postMessage({ab: ab, cb: ab}, [ab]);
-      ok(ab.byteLength == 0, "PostMessage - The size is: 0 == " + ab.byteLength)
+  function port_test() {
+    window.addEventListener('message', receiveMessage, false);
+    function receiveMessage(evt) {
+      ok(evt.data.type == 'OK', evt.data.msg);
+    }
+
+    var a = new MessageChannel();
+    ok(a, "MessageChannel created");
+
+    var div = document.getElementById("content");
+    ok(div, "Parent exists");
+
+    var ifr = document.createElement("iframe");
+    ifr.addEventListener("load", iframeLoaded, false);
+    ifr.setAttribute('src', "iframe_messageChannel_transferable.html");
+    div.appendChild(ifr);
+
+    function iframeLoaded() {
+      ifr.contentWindow.postMessage('foobar!', '*', [a.port2]);
+    }
+
+    a.port1.onmessage = function(evt) {
+      ok(evt.ports.length == 1, "Iframe sent a new port!");
+      evt.ports[0].onmessage = function(evt) {
+        is(evt.data, "hello world!", "Message sent and received!");
+        runTests();
+      }
+
+      evt.ports[0].postMessage("hello world!");
     }
   }
 
+  var tests = [
+    basic_test,
+    port_test
+  ];
+
+  function runTests() {
+    if (!tests.length) {
+      SimpleTest.finish();
+      return;
+    }
+
+    var t = tests.shift();
+    t();
+  }
+
   SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, start);
+  SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTests);
   </script>
 </body>
 </html>
rename from dom/base/test/test_messageChannel_unshipped.html
rename to dom/messagechannel/tests/test_messageChannel_unshipped.html
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_worker.html
@@ -0,0 +1,60 @@
+
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 677638 - basic support</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <iframe name="x" id="x"></iframe>
+  <iframe name="y" id="y"></iframe>
+</div>
+<pre id="test">
+</pre>
+  <script type="application/javascript">
+
+  var tests = [ 0, 3 ];
+
+  function runTests() {
+    if (!tests.length) {
+      SimpleTest.finish();
+      return;
+    }
+
+    var a = new Worker('worker_messageChannel.js');
+    a.onmessage = function(evt) {
+      if (evt.data.type == 'finish') {
+        runTests();
+      } else if (evt.data.type == 'info') {
+        info(evt.data.message);
+      } else if (evt.data.type == 'check') {
+        ok(evt.data.check, evt.data.message);
+      } else if (evt.data.type == 'port') {
+        is(evt.ports.length, 1, "A port has been received!");
+        evt.ports[0].onmessage = function(e) {
+          e.target.postMessage(e.data);
+        }
+      } else if (evt.data.type == 'newport') {
+        var ch = new MessageChannel();
+        ok(ch, "MessageChannel created");
+        ch.port1.postMessage(42);
+        a.postMessage('a gift!', [ch.port2]);
+      }
+    }
+
+    a.postMessage(tests.shift());
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTests);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/worker_messageChannel.js
@@ -0,0 +1,119 @@
+function ok(a, msg) {
+  postMessage({ type: 'check', check: !!a, message: msg });
+}
+
+function is(a, b, msg) {
+  ok (a === b, msg);
+}
+
+function info(msg) {
+  postMessage({ type: 'info', message: msg });
+}
+
+function finish() {
+  postMessage({ type: 'finish' });
+}
+
+function basic()
+{
+  var a = new MessageChannel();
+  ok(a, "MessageChannel created");
+
+  var port1 = a.port1;
+  ok(port1, "MessageChannel.port1 exists");
+  is(port1, a.port1, "MessageChannel.port1 is port1");
+
+  var port2 = a.port2;
+  ok(port2, "MessageChannel.port1 exists");
+  is(port2, a.port2, "MessageChannel.port2 is port2");
+
+  [ 'postMessage', 'start', 'close' ].forEach(function(e) {
+    ok(e in port1, "MessagePort1." + e + " exists");
+    ok(e in port2, "MessagePort2." + e + " exists");
+  });
+
+  runTests();
+}
+
+function sendMessages()
+{
+  var a = new MessageChannel();
+  ok(a, "MessageChannel created");
+
+  a.port1.postMessage("Hello world!");
+  a.port1.onmessage = function(e) {
+    is(e.data, "Hello world!", "The message is back!");
+    runTests();
+  }
+
+  a.port2.onmessage = function(e) {
+    a.port2.postMessage(e.data);
+  }
+}
+
+function transferPort()
+{
+  var a = new MessageChannel();
+  ok(a, "MessageChannel created");
+
+  a.port1.postMessage("Hello world!");
+  a.port1.onmessage = function(e) {
+    is(e.data, "Hello world!", "The message is back!");
+    runTests();
+  }
+
+  postMessage({ type: 'port' }, [a.port2]);
+}
+
+function transferPort2()
+{
+  onmessage = function(evt) {
+    is(evt.ports.length, 1, "A port has been received by the worker");
+    evt.ports[0].onmessage = function(e) {
+      is(e.data, 42, "Data is 42!");
+      runTests();
+    }
+  }
+
+  postMessage({ type: 'newport' });
+}
+
+var tests = [
+  basic,
+  sendMessages,
+  transferPort,
+  transferPort2,
+];
+
+function runTests() {
+  if (!tests.length) {
+    finish();
+    return;
+  }
+
+  var t = tests.shift();
+  t();
+}
+
+var subworker;
+onmessage = function(evt) {
+  if (evt.data == 0) {
+    runTests();
+    return;
+  }
+
+  if (!subworker) {
+    info("Create a subworkers. ID: " + evt.data);
+    subworker = new Worker('worker_messageChannel.js');
+    subworker.onmessage = function(e) {
+      info("Proxy a message to the parent.");
+      postMessage(e.data, e.ports);
+    }
+
+    subworker.postMessage(evt.data - 1);
+    return;
+  }
+
+  info("Dispatch a message to the subworker.");
+  subworker.postMessage(evt.data, evt.ports);
+}
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/worker_messageChannel_any.js
@@ -0,0 +1,7 @@
+onmessage = function(evt) {
+  evt.data.onmessage = function(event) {
+    evt.data.postMessage(event.data);
+  }
+}
+
+postMessage("READY");
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -91,16 +91,17 @@ DIRS += [
     'plugins/ipc',
     'indexedDB',
     'system',
     'ipc',
     'workers',
     'camera',
     'audiochannel',
     'broadcastchannel',
+    'messagechannel',
     'promise',
     'smil',
     'telephony',
     'tv',
     'voicemail',
     'inputmethod',
     'webidl',
     'xbl',
--- a/dom/webidl/MessageChannel.webidl
+++ b/dom/webidl/MessageChannel.webidl
@@ -2,13 +2,14 @@
 /* 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/.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#channel-messaging
  */
 
-[Constructor, Func="MessageChannel::Enabled"]
+[Constructor, Func="MessageChannel::Enabled",
+ Exposed=(Window,Worker)]
 interface MessageChannel {
   readonly attribute MessagePort port1;
   readonly attribute MessagePort port2;
 };
--- a/dom/workers/MessagePort.cpp
+++ b/dom/workers/MessagePort.cpp
@@ -12,16 +12,17 @@
 #include "nsIDOMEvent.h"
 
 #include "SharedWorker.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
 using mozilla::dom::EventHandlerNonNull;
 using mozilla::dom::MessagePortBase;
+using mozilla::dom::MessagePortIdentifier;
 using mozilla::dom::Optional;
 using mozilla::dom::Sequence;
 using mozilla::dom::AutoNoJSAPI;
 using namespace mozilla;
 
 USING_WORKERS_NAMESPACE
 
 namespace {
@@ -91,19 +92,19 @@ MessagePort::MessagePort(WorkerPrivate* 
 }
 
 MessagePort::~MessagePort()
 {
   Close();
 }
 
 void
-MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                            const Optional<Sequence<JS::Value>>& aTransferable,
-                            ErrorResult& aRv)
+MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                         const Optional<Sequence<JS::Value>>& aTransferable,
+                         ErrorResult& aRv)
 {
   AssertCorrectThread();
 
   if (IsClosed()) {
     aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
     return;
   }
 
@@ -193,21 +194,21 @@ MessagePort::SetOnmessage(EventHandlerNo
   }
   else {
     SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
   }
 
   Start();
 }
 
-already_AddRefed<MessagePortBase>
-MessagePort::Clone()
+bool
+MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
 {
   NS_WARNING("Haven't implemented structured clone for these ports yet!");
-  return nullptr;
+  return false;
 }
 
 void
 MessagePort::CloseInternal()
 {
   AssertCorrectThread();
   MOZ_ASSERT(!IsClosed());
   MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty());
--- a/dom/workers/MessagePort.h
+++ b/dom/workers/MessagePort.h
@@ -38,19 +38,19 @@ class MessagePort final : public mozilla
   uint64_t mSerial;
   bool mStarted;
 
 public:
   static bool
   PrefEnabled();
 
   virtual void
-  PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                 const Optional<Sequence<JS::Value>>& aTransferable,
-                 ErrorResult& aRv) override;
+  PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+              const Optional<Sequence<JS::Value>>& aTransferable,
+              ErrorResult& aRv) override;
 
   virtual void
   Start() override;
 
   virtual void
   Close() override;
 
   uint64_t
@@ -66,18 +66,18 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort, DOMEventTargetHelper)
 
   virtual EventHandlerNonNull*
   GetOnmessage() override;
 
   virtual void
   SetOnmessage(EventHandlerNonNull* aCallback) override;
 
-  virtual already_AddRefed<MessagePortBase>
-  Clone() override;
+  virtual bool
+  CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
 
   bool
   IsClosed() const
   {
     return !mSharedWorker && !mWorkerPrivate;
   }
 
   virtual JSObject*
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -7,16 +7,17 @@
 
 #include "ServiceWorkerClient.h"
 #include "ServiceWorkerContainer.h"
 
 #include "mozilla/dom/MessageEvent.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocument.h"
 #include "WorkerPrivate.h"
+#include "WorkerStructuredClone.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::workers;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClient, mOwner)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClient)
@@ -70,26 +71,28 @@ ServiceWorkerClient::WrapObject(JSContex
 }
 
 namespace {
 
 class ServiceWorkerClientPostMessageRunnable final : public nsRunnable
 {
   uint64_t mWindowId;
   JSAutoStructuredCloneBuffer mBuffer;
-  nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
+  WorkerStructuredCloneClosure mClosure;
 
 public:
   ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId,
                                          JSAutoStructuredCloneBuffer&& aData,
-                                         nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+                                         WorkerStructuredCloneClosure& aClosure)
     : mWindowId(aWindowId),
       mBuffer(Move(aData))
   {
-    mClonedObjects.SwapElements(aClonedObjects);
+    mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+    MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
+    mClosure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
   }
 
   NS_IMETHOD
   Run()
   {
     AssertIsOnMainThread();
     nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
     if (!window) {
@@ -113,18 +116,20 @@ public:
 private:
   NS_IMETHOD
   DispatchDOMEvent(JSContext* aCx, ServiceWorkerContainer* aTargetContainer)
   {
     AssertIsOnMainThread();
 
     // Release reference to objects that were AddRef'd for
     // cloning into worker when array goes out of scope.
-    nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-    clonedObjects.SwapElements(mClonedObjects);
+    WorkerStructuredCloneClosure closure;
+    closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
+    MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
+    closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
 
     JS::Rooted<JS::Value> messageData(aCx);
     if (!mBuffer.read(aCx, &messageData,
                       WorkerStructuredCloneCallbacks(true))) {
       xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
       return NS_ERROR_FAILURE;
     }
 
@@ -180,24 +185,25 @@ ServiceWorkerClient::PostMessage(JSConte
       return;
     }
 
     transferable.setObject(*array);
   }
 
   const JSStructuredCloneCallbacks* callbacks = WorkerStructuredCloneCallbacks(false);
 
-  nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
+  WorkerStructuredCloneClosure closure;
 
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
+  if (!buffer.write(aCx, aMessage, transferable, callbacks, &closure)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
-    new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer), clonedObjects);
+    new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer),
+                                               closure);
   nsresult rv = NS_DispatchToMainThread(runnable);
   if (NS_FAILED(rv)) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -47,16 +47,18 @@
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseDebugging.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredClone.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
@@ -100,16 +102,17 @@
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "ServiceWorkerManager.h"
 #include "SharedWorker.h"
 #include "WorkerDebuggerManager.h"
 #include "WorkerFeature.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
+#include "WorkerStructuredClone.h"
 #include "WorkerThread.h"
 
 #ifdef XP_WIN
 #undef PostMessage
 #endif
 
 // JS_MaybeGC will run once every second during normal execution.
 #define PERIODIC_GC_TIMER_DELAY_SEC 1
@@ -506,17 +509,17 @@ ReadFormData(JSContext* aCx,
 
   aFormData.set(formData->WrapObject(aCx, nullptr));
 }
 
 bool
 WriteBlobOrFile(JSContext* aCx,
                 JSStructuredCloneWriter* aWriter,
                 BlobImpl* aBlobOrBlobImpl,
-                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+                WorkerStructuredCloneClosure& aClosure)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aBlobOrBlobImpl);
 
   nsRefPtr<BlobImpl> blobImpl = EnsureBlobForBackgroundManager(aBlobOrBlobImpl);
   MOZ_ASSERT(blobImpl);
 
@@ -524,17 +527,17 @@ WriteBlobOrFile(JSContext* aCx,
 
   if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0)) ||
       NS_WARN_IF(!JS_WriteBytes(aWriter,
                                 &aBlobOrBlobImpl,
                                 sizeof(aBlobOrBlobImpl)))) {
     return false;
   }
 
-  aClonedObjects.AppendElement(aBlobOrBlobImpl);
+  aClosure.mClonedObjects.AppendElement(aBlobOrBlobImpl);
   return true;
 }
 
 // A FormData is serialized as:
 //  - A pair of ints (tag identifying it as a FormData, number of elements in
 //  the FormData)
 //  - for each (key, value) pair:
 //    - pair of ints (is value a file?, 0). If not a file, value is a string.
@@ -542,34 +545,34 @@ WriteBlobOrFile(JSContext* aCx,
 //    - if value is a file:
 //      - write the file/blob
 //    - else:
 //      - string value
 bool
 WriteFormData(JSContext* aCx,
               JSStructuredCloneWriter* aWriter,
               nsFormData* aFormData,
-              nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+              WorkerStructuredCloneClosure& aClosure)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aFormData);
 
   if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FORMDATA, aFormData->Length()))) {
     return false;
   }
 
   class MOZ_STACK_CLASS Closure {
     JSContext* mCx;
     JSStructuredCloneWriter* mWriter;
-    nsTArray<nsCOMPtr<nsISupports>>& mClones;
+    WorkerStructuredCloneClosure& mClones;
 
   public:
     Closure(JSContext* aCx, JSStructuredCloneWriter* aWriter,
-            nsTArray<nsCOMPtr<nsISupports>>& aClones)
+            WorkerStructuredCloneClosure& aClones)
       : mCx(aCx), mWriter(aWriter), mClones(aClones)
     { }
 
     static bool
     Write(const nsString& aName, bool isFile, const nsString& aValue,
           File* aFile, void* aClosure)
     {
       Closure* closure = static_cast<Closure*>(aClosure);
@@ -590,17 +593,17 @@ WriteFormData(JSContext* aCx,
           return false;
         }
       }
 
       return true;
     }
   };
 
-  Closure closure(aCx, aWriter, aClonedObjects);
+  Closure closure(aCx, aWriter, aClosure);
   return aFormData->ForEach(Closure::Write, &closure);
 }
 
 struct WorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
@@ -634,28 +637,26 @@ struct WorkerStructuredCloneCallbacks
   }
 
   static bool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
         JS::Handle<JSObject*> aObj, void* aClosure)
   {
     NS_ASSERTION(aClosure, "Null pointer!");
 
-    // We'll stash any nsISupports pointers that need to be AddRef'd here.
-    auto* clonedObjects =
-      static_cast<nsTArray<nsCOMPtr<nsISupports>>*>(aClosure);
+    auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
 
     // See if this is a Blob/File object.
     {
       nsRefPtr<Blob> blob;
       if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
         BlobImpl* blobImpl = blob->Impl();
         MOZ_ASSERT(blobImpl);
 
-        if (WriteBlobOrFile(aCx, aWriter, blobImpl, *clonedObjects)) {
+        if (WriteBlobOrFile(aCx, aWriter, blobImpl, *closure)) {
           return true;
         }
       }
     }
 
     // See if this is an ImageData object.
     {
       ImageData* imageData = nullptr;
@@ -663,40 +664,121 @@ struct WorkerStructuredCloneCallbacks
         return WriteStructuredCloneImageData(aCx, aWriter, imageData);
       }
     }
 
     // See if this is a FormData object.
     {
       nsFormData* formData = nullptr;
       if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
-        if (WriteFormData(aCx, aWriter, formData, *clonedObjects)) {
+        if (WriteFormData(aCx, aWriter, formData, *closure)) {
           return true;
         }
       }
     }
 
     Error(aCx, 0);
     return false;
   }
 
   static void
   Error(JSContext* aCx, uint32_t /* aErrorId */)
   {
     Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
   }
+
+  static bool
+  ReadTransfer(JSContext* aCx, JSStructuredCloneReader* aReader,
+               uint32_t aTag, void* aContent, uint64_t aExtraData,
+               void* aClosure, JS::MutableHandle<JSObject*> aReturnObject)
+  {
+    MOZ_ASSERT(aClosure);
+
+    auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
+
+    if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+      MOZ_ASSERT(!aContent);
+      MOZ_ASSERT(aExtraData < closure->mMessagePortIdentifiers.Length());
+
+      ErrorResult rv;
+      nsRefPtr<MessagePortBase> port =
+        dom::MessagePort::Create(closure->mParentWindow,
+                                 closure->mMessagePortIdentifiers[aExtraData],
+                                 rv);
+
+      if (NS_WARN_IF(rv.Failed())) {
+        return false;
+      }
+
+      closure->mMessagePorts.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;
+  }
+
+  static bool
+  Transfer(JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
+           uint32_t* aTag, JS::TransferableOwnership* aOwnership,
+           void** aContent, uint64_t *aExtraData)
+  {
+    MOZ_ASSERT(aClosure);
+
+    auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
+
+    MessagePortBase* port;
+    nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
+    if (NS_SUCCEEDED(rv)) {
+      if (NS_WARN_IF(closure->mTransferredPorts.Contains(port))) {
+        // No duplicates.
+        return false;
+      }
+
+      MessagePortIdentifier identifier;
+      if (!port->CloneAndDisentangle(identifier)) {
+        return false;
+      }
+
+      closure->mMessagePortIdentifiers.AppendElement(identifier);
+      closure->mTransferredPorts.AppendElement(port);
+
+      *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
+      *aOwnership = JS::SCTAG_TMO_CUSTOM;
+      *aContent = nullptr;
+      *aExtraData = closure->mMessagePortIdentifiers.Length() - 1;
+
+      return true;
+    }
+
+    return false;
+  }
+
+  static void
+  FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership,
+               void *aContent, uint64_t aExtraData, void* aClosure)
+  {
+    // Nothing to do.
+  }
 };
 
 const JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
   WorkerStructuredCloneCallbacks::Read,
   WorkerStructuredCloneCallbacks::Write,
   WorkerStructuredCloneCallbacks::Error,
-  nullptr,
-  nullptr,
-  nullptr
+  WorkerStructuredCloneCallbacks::ReadTransfer,
+  WorkerStructuredCloneCallbacks::Transfer,
+  WorkerStructuredCloneCallbacks::FreeTransfer
 };
 
 struct MainThreadWorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
   {
@@ -726,30 +808,28 @@ struct MainThreadWorkerStructuredCloneCa
   static bool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
         JS::Handle<JSObject*> aObj, void* aClosure)
   {
     AssertIsOnMainThread();
 
     NS_ASSERTION(aClosure, "Null pointer!");
 
-    // We'll stash any nsISupports pointers that need to be AddRef'd here.
-    auto* clonedObjects =
-      static_cast<nsTArray<nsCOMPtr<nsISupports>>*>(aClosure);
+    auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
 
     // See if this is a Blob/File object.
     {
       nsRefPtr<Blob> blob;
       if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
         BlobImpl* blobImpl = blob->Impl();
         MOZ_ASSERT(blobImpl);
 
         if (!blobImpl->MayBeClonedToOtherThreads()) {
           NS_WARNING("Not all the blob implementations can be sent between threads.");
-        } else if (WriteBlobOrFile(aCx, aWriter, blobImpl, *clonedObjects)) {
+        } else if (WriteBlobOrFile(aCx, aWriter, blobImpl, *closure)) {
           return true;
         }
       }
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
   }
@@ -762,19 +842,19 @@ struct MainThreadWorkerStructuredCloneCa
     NS_DOMStructuredCloneError(aCx, aErrorId);
   }
 };
 
 const JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = {
   MainThreadWorkerStructuredCloneCallbacks::Read,
   MainThreadWorkerStructuredCloneCallbacks::Write,
   MainThreadWorkerStructuredCloneCallbacks::Error,
-  nullptr,
-  nullptr,
-  nullptr
+  WorkerStructuredCloneCallbacks::ReadTransfer,
+  WorkerStructuredCloneCallbacks::Transfer,
+  WorkerStructuredCloneCallbacks::FreeTransfer
 };
 
 struct ChromeWorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
   {
@@ -795,19 +875,19 @@ struct ChromeWorkerStructuredCloneCallba
     return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId);
   }
 };
 
 const JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = {
   ChromeWorkerStructuredCloneCallbacks::Read,
   ChromeWorkerStructuredCloneCallbacks::Write,
   ChromeWorkerStructuredCloneCallbacks::Error,
-  nullptr,
-  nullptr,
-  nullptr
+  WorkerStructuredCloneCallbacks::ReadTransfer,
+  WorkerStructuredCloneCallbacks::Transfer,
+  WorkerStructuredCloneCallbacks::FreeTransfer
 };
 
 struct MainThreadChromeWorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
   {
@@ -1148,55 +1228,71 @@ private:
 
     aWorkerPrivate->CloseHandlerFinished();
   }
 };
 
 class MessageEventRunnable final : public WorkerRunnable
 {
   JSAutoStructuredCloneBuffer mBuffer;
-  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+  WorkerStructuredCloneClosure mClosure;
   uint64_t mMessagePortSerial;
   bool mToMessagePort;
 
   // This is only used for messages dispatched to a service worker.
   nsAutoPtr<ServiceWorkerClientInfo> mEventSource;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
                        TargetAndBusyBehavior aBehavior,
-                       JSAutoStructuredCloneBuffer&& aData,
-                       nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                        bool aToMessagePort, uint64_t aMessagePortSerial)
   : WorkerRunnable(aWorkerPrivate, aBehavior)
-  , mBuffer(Move(aData))
   , mMessagePortSerial(aMessagePortSerial)
   , mToMessagePort(aToMessagePort)
   {
-    mClonedObjects.SwapElements(aClonedObjects);
+  }
+
+  bool
+  Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
+        JS::Handle<JS::Value> aTransferredValue,
+        const JSStructuredCloneCallbacks *aCallbacks)
+   {
+    bool ok = mBuffer.write(aCx, aValue, aTransferredValue, aCallbacks,
+                            &mClosure);
+    // This hashtable has to be empty because it could contain MessagePort
+    // objects that cannot be freed on a different thread.
+    mClosure.mTransferredPorts.Clear();
+    return ok;
   }
 
   void
   SetMessageSource(ServiceWorkerClientInfo* aSource)
   {
     mEventSource = aSource;
   }
 
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                    DOMEventTargetHelper* aTarget, bool aIsMainThread)
   {
     // Release reference to objects that were AddRef'd for
     // cloning into worker when array goes out of scope.
-    nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-    clonedObjects.SwapElements(mClonedObjects);
+    WorkerStructuredCloneClosure closure;
+    closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
+    MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
+    closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
+
+    if (aIsMainThread) {
+      closure.mParentWindow = do_QueryInterface(aTarget->GetParentObject());
+    }
 
     JS::Rooted<JS::Value> messageData(aCx);
     if (!mBuffer.read(aCx, &messageData,
-                      workers::WorkerStructuredCloneCallbacks(aIsMainThread))) {
+                      workers::WorkerStructuredCloneCallbacks(aIsMainThread),
+                      &closure)) {
       xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
       return false;
     }
 
     nsRefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
     nsresult rv =
       event->InitMessageEvent(NS_LITERAL_STRING("message"),
                               false /* non-bubbling */,
@@ -1212,17 +1308,18 @@ public:
     }
 
     if (NS_FAILED(rv)) {
       xpc::Throw(aCx, rv);
       return false;
     }
 
     event->SetTrusted(true);
-
+    event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
+                                        closure.mMessagePorts));
     nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
 
     nsEventStatus dummy = nsEventStatus_eIgnore;
     aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
     return true;
   }
 
 private:
@@ -1238,17 +1335,17 @@ private:
         return true;
       }
 
       if (mToMessagePort) {
         return
           aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
                                                             mMessagePortSerial,
                                                             Move(mBuffer),
-                                                            mClonedObjects);
+                                                            mClosure);
       }
 
       if (aWorkerPrivate->IsFrozen()) {
         aWorkerPrivate->QueueRunnable(this);
         return true;
       }
 
       aWorkerPrivate->AssertInnerWindowIsCorrect();
@@ -3374,29 +3471,26 @@ WorkerPrivateParent<Derived>::PostMessag
       JS_NewArrayObject(aCx, elements);
     if (!array) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     transferable.setObject(*array);
   }
 
-  nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-
-  JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
-    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
-    return;
-  }
-
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(ParentAsWorkerPrivate(),
                              WorkerRunnable::WorkerThreadModifyBusyCount,
-                             Move(buffer), clonedObjects, aToMessagePort,
-                             aMessagePortSerial);
+                             aToMessagePort, aMessagePortSerial);
+
+  if (!runnable->Write(aCx, aMessage, transferable, callbacks)) {
+    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+    return;
+  }
+
   runnable->SetMessageSource(aClientInfo);
 
   if (!runnable->Dispatch(aCx)) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
 template <class Derived>
@@ -3427,67 +3521,68 @@ WorkerPrivateParent<Derived>::PostMessag
                       nullptr, aRv);
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
                                 JSContext* aCx, uint64_t aMessagePortSerial,
                                 JSAutoStructuredCloneBuffer&& aBuffer,
-                                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+                                WorkerStructuredCloneClosure& aClosure)
 {
   AssertIsOnMainThread();
 
   JSAutoStructuredCloneBuffer buffer(Move(aBuffer));
 
-  nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-  clonedObjects.SwapElements(aClonedObjects);
+  WorkerStructuredCloneClosure closure;
+  closure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+  MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
+  closure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
 
   SharedWorker* sharedWorker;
   if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
     // SharedWorker has already been unregistered?
     return true;
   }
 
   nsRefPtr<MessagePort> port = sharedWorker->Port();
   NS_ASSERTION(port, "SharedWorkers always have a port!");
 
   if (port->IsClosed()) {
     return true;
   }
 
+  closure.mParentWindow = do_QueryInterface(port->GetParentObject());
+
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(port->GetParentObject()))) {
     return false;
   }
   JSContext* cx = jsapi.cx();
 
   JS::Rooted<JS::Value> data(cx);
-  if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) {
+  if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true),
+                   &closure)) {
     return false;
   }
 
   buffer.clear();
 
   nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr);
   nsresult rv =
     event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
                             EmptyString(), EmptyString(), nullptr);
   if (NS_FAILED(rv)) {
     xpc::Throw(cx, rv);
     return false;
   }
 
   event->SetTrusted(true);
 
-  nsTArray<nsRefPtr<MessagePortBase>> ports;
-  ports.AppendElement(port);
-
-  nsRefPtr<MessagePortList> portList = new MessagePortList(port, ports);
-  event->SetPorts(portList);
+  event->SetPorts(new MessagePortList(port, closure.mMessagePorts));
 
   nsCOMPtr<nsIDOMEvent> domEvent;
   CallQueryInterface(event.get(), getter_AddRefs(domEvent));
   NS_ASSERTION(domEvent, "This should never fail!");
 
   bool ignored;
   rv = port->DispatchEvent(domEvent, &ignored);
   if (NS_FAILED(rv)) {
@@ -6177,29 +6272,26 @@ WorkerPrivate::PostMessageToParentIntern
     transferable.setObject(*array);
   }
 
   const JSStructuredCloneCallbacks* callbacks =
     IsChromeWorker() ?
     &gChromeWorkerStructuredCloneCallbacks :
     &gWorkerStructuredCloneCallbacks;
 
-  nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-
-  JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
-    aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
-    return;
-  }
-
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(this,
                              WorkerRunnable::ParentThreadUnchangedBusyCount,
-                             Move(buffer), clonedObjects, aToMessagePort,
-                             aMessagePortSerial);
+                             aToMessagePort, aMessagePortSerial);
+
+  if (!runnable->Write(aCx, aMessage, transferable, callbacks)) {
+    aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
+    return;
+  }
+
   if (!runnable->Dispatch(aCx)) {
     aRv = NS_ERROR_FAILURE;
   }
 }
 
 void
 WorkerPrivate::PostMessageToParentMessagePort(
                              JSContext* aCx,
@@ -7343,9 +7435,25 @@ ChromeWorkerStructuredCloneCallbacks(boo
   return aMainRuntime ?
          &gMainThreadChromeWorkerStructuredCloneCallbacks :
          &gChromeWorkerStructuredCloneCallbacks;
 }
 
 // Force instantiation.
 template class WorkerPrivateParent<WorkerPrivate>;
 
+WorkerStructuredCloneClosure::WorkerStructuredCloneClosure()
+{}
+
+WorkerStructuredCloneClosure::~WorkerStructuredCloneClosure()
+{}
+
+void
+WorkerStructuredCloneClosure::Clear()
+{
+  mParentWindow = nullptr;
+  mClonedObjects.Clear();
+  mMessagePorts.Clear();
+  mMessagePortIdentifiers.Clear();
+  mTransferredPorts.Clear();
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -68,16 +68,17 @@ class MessagePort;
 class SharedWorker;
 class ServiceWorkerClientInfo;
 class WorkerControlRunnable;
 class WorkerDebugger;
 class WorkerDebuggerGlobalScope;
 class WorkerGlobalScope;
 class WorkerPrivate;
 class WorkerRunnable;
+class WorkerStructuredCloneClosure;
 class WorkerThread;
 
 // SharedMutex is a small wrapper around an (internal) reference-counted Mutex
 // object. It exists to avoid changing a lot of code to use Mutex* instead of
 // Mutex&.
 class SharedMutex
 {
   typedef mozilla::Mutex Mutex;
@@ -344,17 +345,17 @@ public:
                            const Optional<Sequence<JS::Value> >& aTransferable,
                            ErrorResult& aRv);
 
   bool
   DispatchMessageEventToMessagePort(
                                JSContext* aCx,
                                uint64_t aMessagePortSerial,
                                JSAutoStructuredCloneBuffer&& aBuffer,
-                               nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
+                               WorkerStructuredCloneClosure& aClosure);
 
   void
   UpdateRuntimeOptions(JSContext* aCx,
                        const JS::RuntimeOptions& aRuntimeOptions);
 
   void
   UpdateLanguages(JSContext* aCx, const nsTArray<nsString>& aLanguages);
 
new file mode 100644
--- /dev/null
+++ b/dom/workers/WorkerStructuredClone.h
@@ -0,0 +1,53 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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_workers_WorkerStructuredClone_h
+#define mozilla_dom_workers_WorkerStructuredClone_h
+
+#include "Workers.h"
+#include "mozilla/dom/PMessagePort.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortBase;
+
+namespace workers {
+
+// This class is implemented in WorkerPrivate.cpp
+class WorkerStructuredCloneClosure final
+{
+private:
+  WorkerStructuredCloneClosure(const WorkerStructuredCloneClosure&) = delete;
+  WorkerStructuredCloneClosure & operator=(const WorkerStructuredCloneClosure&) = delete;
+
+public:
+  WorkerStructuredCloneClosure();
+  ~WorkerStructuredCloneClosure();
+
+  void Clear();
+
+  // This can be null if the MessagePort is created in a worker.
+  nsCOMPtr<nsPIDOMWindow> mParentWindow;
+
+  nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
+
+  // The transferred ports.
+  nsTArray<nsRefPtr<MessagePortBase>> mMessagePorts;
+
+  // Information for the transferring.
+  nsTArray<MessagePortIdentifier> mMessagePortIdentifiers;
+
+  // To avoid duplicates in the transferred ports.
+  nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+};
+
+} // workers namespace
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_workers_WorkerStructuredClone_h
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -22,16 +22,17 @@
 #include "nsContentUtils.h"
 #include "nsFormData.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 
 #include "RuntimeService.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
+#include "WorkerStructuredClone.h"
 #include "XMLHttpRequestUpload.h"
 
 using namespace mozilla;
 
 using namespace mozilla::dom;
 USING_WORKERS_NAMESPACE
 
 /**
@@ -408,17 +409,17 @@ private:
     }
 };
 
 class EventRunnable final : public MainThreadProxyRunnable
 {
   nsString mType;
   nsString mResponseType;
   JSAutoStructuredCloneBuffer mResponseBuffer;
-  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+  WorkerStructuredCloneClosure mResponseClosure;
   JS::Heap<JS::Value> mResponse;
   nsString mResponseText;
   nsString mResponseURL;
   nsCString mStatusText;
   uint64_t mLoaded;
   uint64_t mTotal;
   uint32_t mEventStreamId;
   uint32_t mStatus;
@@ -789,32 +790,34 @@ private:
   nsresult
   MainThreadRunInternal();
 };
 
 class SendRunnable final : public WorkerThreadProxySyncRunnable
 {
   nsString mStringBody;
   JSAutoStructuredCloneBuffer mBody;
-  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+  WorkerStructuredCloneClosure mClosure;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   bool mHasUploadListeners;
 
 public:
   SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
                const nsAString& aStringBody, JSAutoStructuredCloneBuffer&& aBody,
-               nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects,
+               WorkerStructuredCloneClosure& aClosure,
                nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   , mStringBody(aStringBody)
   , mBody(Move(aBody))
   , mSyncLoopTarget(aSyncLoopTarget)
   , mHasUploadListeners(aHasUploadListeners)
   {
-    mClonedObjects.SwapElements(aClonedObjects);
+    mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+    MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
+    MOZ_ASSERT(aClosure.mMessagePortIdentifiers.IsEmpty());
   }
 
 private:
   ~SendRunnable()
   { }
 
   virtual nsresult
   MainThreadRun() override;
@@ -1224,21 +1227,23 @@ EventRunnable::PreDispatch(JSContext* aC
 
         if (doClone) {
           // Anything subject to GC must be cloned.
           const JSStructuredCloneCallbacks* callbacks =
             aWorkerPrivate->IsChromeWorker() ?
             workers::ChromeWorkerStructuredCloneCallbacks(true) :
             workers::WorkerStructuredCloneCallbacks(true);
 
-          nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+          WorkerStructuredCloneClosure closure;
 
           if (mResponseBuffer.write(aCx, response, transferable, callbacks,
-                                    &clonedObjects)) {
-            mClonedObjects.SwapElements(clonedObjects);
+                                    &closure)) {
+            mResponseClosure.mClonedObjects.SwapElements(closure.mClonedObjects);
+            MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
+            MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
           } else {
             NS_WARNING("Failed to clone response!");
             mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
             mProxy->mArrayBufferResponseWasTransferred = false;
           }
         }
       }
     }
@@ -1336,21 +1341,23 @@ EventRunnable::WorkerRun(JSContext* aCx,
 
         JSAutoStructuredCloneBuffer responseBuffer(Move(mResponseBuffer));
 
         const JSStructuredCloneCallbacks* callbacks =
           aWorkerPrivate->IsChromeWorker() ?
           workers::ChromeWorkerStructuredCloneCallbacks(false) :
           workers::WorkerStructuredCloneCallbacks(false);
 
-        nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
-        clonedObjects.SwapElements(mClonedObjects);
+        WorkerStructuredCloneClosure closure;
+        closure.mClonedObjects.SwapElements(mResponseClosure.mClonedObjects);
+        MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
+        MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
 
         JS::Rooted<JS::Value> response(aCx);
-        if (!responseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
+        if (!responseBuffer.read(aCx, &response, callbacks, &closure)) {
           return false;
         }
 
         state->mResponse = response;
       }
       else {
         state->mResponse = mResponse;
       }
@@ -1521,27 +1528,27 @@ SendRunnable::MainThreadRun()
     nsresult rv = NS_OK;
 
     const JSStructuredCloneCallbacks* callbacks =
       mWorkerPrivate->IsChromeWorker() ?
       workers::ChromeWorkerStructuredCloneCallbacks(true) :
       workers::WorkerStructuredCloneCallbacks(true);
 
     JS::Rooted<JS::Value> body(cx);
-    if (mBody.read(cx, &body, callbacks, &mClonedObjects)) {
+    if (mBody.read(cx, &body, callbacks, &mClosure)) {
       if (NS_FAILED(xpc->JSValToVariant(cx, body, getter_AddRefs(variant)))) {
         rv = NS_ERROR_DOM_INVALID_STATE_ERR;
       }
     }
     else {
       rv = NS_ERROR_DOM_DATA_CLONE_ERR;
     }
 
     mBody.clear();
-    mClonedObjects.Clear();
+    mClosure.Clear();
 
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     nsCOMPtr<nsIWritableVariant> wvariant =
       do_CreateInstance(NS_VARIANT_CONTRACTID);
     NS_ENSURE_TRUE(wvariant, NS_ERROR_UNEXPECTED);
 
@@ -1841,17 +1848,17 @@ XMLHttpRequest::Unpin()
   mRooted = false;
 
   NS_RELEASE_THIS();
 }
 
 void
 XMLHttpRequest::SendInternal(const nsAString& aStringBody,
                              JSAutoStructuredCloneBuffer&& aBody,
-                             nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
+                             WorkerStructuredCloneClosure& aClosure,
                              ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   // No send() calls when open is running.
   if (mProxy->mOpening) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
@@ -1875,17 +1882,17 @@ XMLHttpRequest::SendInternal(const nsASt
   }
 
   mProxy->mOuterChannelId++;
 
   JSContext* cx = mWorkerPrivate->GetJSContext();
 
   nsRefPtr<SendRunnable> runnable =
     new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
-                     aClonedObjects, syncLoopTarget, hasUploadListeners);
+                     aClosure, syncLoopTarget, hasUploadListeners);
   if (!runnable->Dispatch(cx)) {
     // Dispatch() may have spun the event loop and we may have already unrooted.
     // If so we don't want autoUnpin to try again.
     if (!mRooted) {
       autoUnpin.Clear();
     }
     aRv.Throw(NS_ERROR_FAILURE);
     return;
@@ -2097,19 +2104,19 @@ XMLHttpRequest::Send(ErrorResult& aRv)
 
   if (!mProxy) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Nothing to clone.
   JSAutoStructuredCloneBuffer buffer;
-  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
-
-  SendInternal(NullString(), Move(buffer), clonedObjects, aRv);
+  WorkerStructuredCloneClosure closure;
+
+  SendInternal(NullString(), Move(buffer), closure, aRv);
 }
 
 void
 XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
@@ -2119,19 +2126,19 @@ XMLHttpRequest::Send(const nsAString& aB
 
   if (!mProxy) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Nothing to clone.
   JSAutoStructuredCloneBuffer buffer;
-  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
-
-  SendInternal(aBody, Move(buffer), clonedObjects, aRv);
+  WorkerStructuredCloneClosure closure;
+
+  SendInternal(aBody, Move(buffer), closure, aRv);
 }
 
 void
 XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
 {
   JSContext* cx = mWorkerPrivate->GetJSContext();
 
   MOZ_ASSERT(aBody);
@@ -2162,25 +2169,25 @@ XMLHttpRequest::Send(JS::Handle<JSObject
     valToClone.setString(bodyStr);
   }
 
   const JSStructuredCloneCallbacks* callbacks =
     mWorkerPrivate->IsChromeWorker() ?
     ChromeWorkerStructuredCloneCallbacks(false) :
     WorkerStructuredCloneCallbacks(false);
 
-  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+  WorkerStructuredCloneClosure closure;
 
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(cx, valToClone, callbacks, &clonedObjects)) {
+  if (!buffer.write(cx, valToClone, callbacks, &closure)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
+  SendInternal(EmptyString(), Move(buffer), closure, aRv);
 }
 
 void
 XMLHttpRequest::Send(Blob& aBody, ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   JSContext* cx = mWorkerPrivate->GetJSContext();
 
@@ -2208,25 +2215,25 @@ XMLHttpRequest::Send(Blob& aBody, ErrorR
     return;
   }
 
   const JSStructuredCloneCallbacks* callbacks =
     mWorkerPrivate->IsChromeWorker() ?
     ChromeWorkerStructuredCloneCallbacks(false) :
     WorkerStructuredCloneCallbacks(false);
 
-  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+  WorkerStructuredCloneClosure closure;
 
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(cx, value, callbacks, &clonedObjects)) {
+  if (!buffer.write(cx, value, callbacks, &closure)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
+  SendInternal(EmptyString(), Move(buffer), closure, aRv);
 }
 
 void
 XMLHttpRequest::Send(nsFormData& aBody, ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   JSContext* cx = mWorkerPrivate->GetJSContext();
 
@@ -2246,25 +2253,24 @@ XMLHttpRequest::Send(nsFormData& aBody, 
     return;
   }
 
   const JSStructuredCloneCallbacks* callbacks =
     mWorkerPrivate->IsChromeWorker() ?
     ChromeWorkerStructuredCloneCallbacks(false) :
     WorkerStructuredCloneCallbacks(false);
 
-  nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(cx, value, callbacks, &clonedObjects)) {
+  WorkerStructuredCloneClosure closure;
+  if (!buffer.write(cx, value, callbacks, &closure)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
+  SendInternal(EmptyString(), Move(buffer), closure, aRv);
 }
 
 void
 XMLHttpRequest::Send(const ArrayBuffer& aBody, ErrorResult& aRv)
 {
   JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
   return Send(obj, aRv);
 }
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -23,16 +23,17 @@ class Blob;
 }
 }
 
 BEGIN_WORKERS_NAMESPACE
 
 class Proxy;
 class XMLHttpRequestUpload;
 class WorkerPrivate;
+class WorkerStructuredCloneClosure;
 
 class XMLHttpRequest final: public nsXHREventTarget,
                             public WorkerFeature
 {
 public:
   struct StateData
   {
     nsString mResponseText;
@@ -287,15 +288,15 @@ private:
   void
   DispatchPrematureAbortEvent(EventTarget* aTarget,
                               const nsAString& aEventType, bool aUploadTarget,
                               ErrorResult& aRv);
 
   void
   SendInternal(const nsAString& aStringBody,
                JSAutoStructuredCloneBuffer&& aBody,
-               nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
+               WorkerStructuredCloneClosure& aClosure,
                ErrorResult& aRv);
 };
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_xmlhttprequest_h__
--- a/dom/workers/test/sharedWorker_sharedWorker.js
+++ b/dom/workers/test/sharedWorker_sharedWorker.js
@@ -74,15 +74,15 @@ onconnect = function(event) {
 
   event.ports[0].onmessage = function(event) {
     if (!(event instanceof MessageEvent)) {
       throw new Error("'message' event is not a MessageEvent!");
     }
     if (!("ports" in event)) {
       throw new Error("'message' event doesn't have a 'ports' property!");
     }
-    if (!(event.ports === null)) {
-      throw new Error("'message' event has a non-null 'ports' property!");
+    if (event.ports === null) {
+      throw new Error("'message' event has a null 'ports' property!");
     }
     event.target.postMessage(event.data);
     throw new Error(event.data);
   };
 };
--- a/ipc/glue/BackgroundChild.h
+++ b/ipc/glue/BackgroundChild.h
@@ -10,16 +10,17 @@
 #include "mozilla/ipc/Transport.h"
 
 class nsIDOMBlob;
 class nsIIPCBackgroundChildCreateCallback;
 
 namespace mozilla {
 namespace dom {
 
+class BlobImpl;
 class ContentChild;
 class ContentParent;
 class PBlobChild;
 
 } // namespace dom
 
 namespace ipc {
 
@@ -62,16 +63,20 @@ public:
   // See above.
   static bool
   GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
 
   static mozilla::dom::PBlobChild*
   GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
                           nsIDOMBlob* aBlob);
 
+  static mozilla::dom::PBlobChild*
+  GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
+                              mozilla::dom::BlobImpl* aBlobImpl);
+
   // See above.
   static void
   CloseForCurrentThread();
 
 private:
   // Only called by ContentChild or ContentParent.
   static void
   Startup();
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -9,16 +9,17 @@
 #include "ServiceWorkerManagerChild.h"
 #include "FileDescriptorSetChild.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "mozilla/net/PUDPSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "nsID.h"
 #include "nsTraceRefcnt.h"
 
 namespace {
@@ -335,16 +336,38 @@ BackgroundChildImpl::AllocPMediaChild()
 }
 
 bool
 BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
 {
   return media::DeallocPMediaChild(aActor);
 }
 
+// -----------------------------------------------------------------------------
+// MessageChannel/MessagePort API
+// -----------------------------------------------------------------------------
+
+dom::PMessagePortChild*
+BackgroundChildImpl::AllocPMessagePortChild(const nsID& aUUID,
+                                            const nsID& aDestinationUUID,
+                                            const uint32_t& aSequenceID)
+{
+  nsRefPtr<dom::MessagePortChild> agent = new dom::MessagePortChild();
+  return agent.forget().take();
+}
+
+bool
+BackgroundChildImpl::DeallocPMessagePortChild(PMessagePortChild* aActor)
+{
+  nsRefPtr<dom::MessagePortChild> child =
+    dont_AddRef(static_cast<dom::MessagePortChild*>(aActor));
+  MOZ_ASSERT(child);
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -116,16 +116,23 @@ protected:
   virtual bool
   DeallocPCacheChild(dom::cache::PCacheChild* aActor) override;
 
   virtual dom::cache::PCacheStreamControlChild*
   AllocPCacheStreamControlChild() override;
 
   virtual bool
   DeallocPCacheStreamControlChild(dom::cache::PCacheStreamControlChild* aActor) override;
+
+  virtual PMessagePortChild*
+  AllocPMessagePortChild(const nsID& aUUID, const nsID& aDestinationUUID,
+                         const uint32_t& aSequenceID) override;
+
+  virtual bool
+  DeallocPMessagePortChild(PMessagePortChild* aActor) override;
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -909,27 +909,37 @@ BackgroundChild::GetOrCreateForCurrentTh
   return ChildImpl::GetOrCreateForCurrentThread(aCallback);
 }
 
 // static
 PBlobChild*
 BackgroundChild::GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
                                          nsIDOMBlob* aBlob)
 {
+  MOZ_ASSERT(aBlob);
+
+  nsRefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
+  MOZ_ASSERT(blobImpl);
+
+  return GetOrCreateActorForBlobImpl(aBackgroundActor, blobImpl);
+}
+
+// static
+PBlobChild*
+BackgroundChild::GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
+                                             BlobImpl* aBlobImpl)
+{
   MOZ_ASSERT(aBackgroundActor);
-  MOZ_ASSERT(aBlob);
+  MOZ_ASSERT(aBlobImpl);
   MOZ_ASSERT(GetForCurrentThread(),
              "BackgroundChild not created on this thread yet!");
   MOZ_ASSERT(aBackgroundActor == GetForCurrentThread(),
              "BackgroundChild is bound to a different thread!");
 
-  nsRefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
-  MOZ_ASSERT(blobImpl);
-
-  BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, blobImpl);
+  BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, aBlobImpl);
   if (NS_WARN_IF(!actor)) {
     return nullptr;
   }
 
   return actor;
 }
 
 // static
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -6,16 +6,17 @@
 
 #include "BroadcastChannelParent.h"
 #include "FileDescriptorSetParent.h"
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PBlobParent.h"
+#include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
@@ -34,16 +35,18 @@
 #else
 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false)
 #endif
 
 using mozilla::ipc::AssertIsOnBackgroundThread;
 using mozilla::dom::cache::PCacheParent;
 using mozilla::dom::cache::PCacheStorageParent;
 using mozilla::dom::cache::PCacheStreamControlParent;
+using mozilla::dom::MessagePortParent;
+using mozilla::dom::PMessagePortParent;
 using mozilla::dom::UDPSocketParent;
 
 namespace {
 
 void
 AssertIsInMainProcess()
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@@ -558,16 +561,51 @@ BackgroundParentImpl::AllocPCacheStreamC
 
 bool
 BackgroundParentImpl::DeallocPCacheStreamControlParent(PCacheStreamControlParent* aActor)
 {
   dom::cache::DeallocPCacheStreamControlParent(aActor);
   return true;
 }
 
+PMessagePortParent*
+BackgroundParentImpl::AllocPMessagePortParent(const nsID& aUUID,
+                                              const nsID& aDestinationUUID,
+                                              const uint32_t& aSequenceID)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return new MessagePortParent(aUUID);
+}
+
+bool
+BackgroundParentImpl::RecvPMessagePortConstructor(PMessagePortParent* aActor,
+                                                  const nsID& aUUID,
+                                                  const nsID& aDestinationUUID,
+                                                  const uint32_t& aSequenceID)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  MessagePortParent* mp = static_cast<MessagePortParent*>(aActor);
+  return mp->Entangle(aDestinationUUID, aSequenceID);
+}
+
+bool
+BackgroundParentImpl::DeallocPMessagePortParent(PMessagePortParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<MessagePortParent*>(aActor);
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -124,14 +124,28 @@ protected:
   AllocPUDPSocketParent(const OptionalPrincipalInfo& pInfo,
                         const nsCString& aFilter) override;
   virtual bool
   RecvPUDPSocketConstructor(PUDPSocketParent*,
                             const OptionalPrincipalInfo& aPrincipalInfo,
                             const nsCString& aFilter) override;
   virtual bool
   DeallocPUDPSocketParent(PUDPSocketParent*) override;
+
+  virtual PMessagePortParent*
+  AllocPMessagePortParent(const nsID& aUUID,
+                          const nsID& aDestinationUUID,
+                          const uint32_t& aSequenceID) override;
+
+  virtual bool
+  RecvPMessagePortConstructor(PMessagePortParent* aActor,
+                              const nsID& aUUID,
+                              const nsID& aDestinationUUID,
+                              const uint32_t& aSequenceID) override;
+
+  virtual bool
+  DeallocPMessagePortParent(PMessagePortParent* aActor) override;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -5,16 +5,17 @@
 include protocol PBackgroundIDBFactory;
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
+include protocol PMessagePort;
 include protocol PMedia;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
@@ -30,16 +31,17 @@ sync protocol PBackground
   manages PBackgroundIDBFactory;
   manages PBackgroundTest;
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
+  manages PMessagePort;
   manages PMedia;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
@@ -54,16 +56,18 @@ parent:
                     bool privateBrowsing);
 
   PServiceWorkerManager();
 
   ShutdownServiceWorkerRegistrar();
 
   PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
 
+  PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
+
 child:
   PCache();
   PCacheStreamControl();
 
 both:
   PBlob(BlobConstructorParams params);
 
   PFileDescriptorSet(FileDescriptor fd);
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -143,17 +143,17 @@ JS_ReadStructuredClone(JSContext* cx, ui
 JS_PUBLIC_API(bool)
 JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, uint64_t** datap, size_t* nbytesp,
                         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);
+                        void *closure, bool freeData = true);
 
 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);
 
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1903,20 +1903,22 @@ JS_WriteStructuredClone(JSContext* cx, H
         optionalCallbacks :
         cx->runtime()->structuredCloneCallbacks;
     return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable);
 }
 
 JS_PUBLIC_API(bool)
 JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
-                        void* closure)
+                        void* closure, bool freeData)
 {
     DiscardTransferables(data, nbytes, optionalCallbacks, closure);
-    js_free(data);
+    if (freeData) {
+      js_free(data);
+    }
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes,
                                    bool* hasTransferable)
 {
     *hasTransferable = StructuredCloneHasTransferObjects(data, nbytes);
deleted file mode 100644
--- a/testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/event-ports-dedicated.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[event-ports-dedicated.html]
-  type: testharness
-  [e.ports in dedicated worker]
-    expected: FAIL
-