Bug 911972 - MessagePort and MessageChannel in workers, r=smaug, r=bent
☠☠ backed out by 53a99d02925f ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 15 Jun 2015 14:08:25 +0100
changeset 248867 1d67d747b3eba11d8a9ba766abb19eb2ebcd0676
parent 248866 2cba58b2e1b402536eeb4fdbc70bf5cfc14b9495
child 248868 cb9d76fedab720989e6ad9691314e126d64b9bdf
push id28909
push userryanvm@gmail.com
push dateMon, 15 Jun 2015 19:55:33 +0000
treeherdermozilla-central@cd0d976e5f5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, bent
bugs911972
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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,389 @@
+/* -*- 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;
+
+  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;
@@ -7146,16 +7155,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,
@@ -2433,16 +2439,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
@@ -281,25 +277,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
-