Bug 1204775 - SharedWorker.port should be a 'real' MessagePort, r=khuey
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 16 Sep 2015 11:27:56 +0800
changeset 262778 faf94ffc0c5ad57c2e4f275e5789f08cf2b33831
parent 262777 7c1d4c3e68ad1aaaa681bded5629e3c682e70d56
child 262779 7fedb2bf7fcb2db25ba3bcc27157f0fd5cc28025
push id15207
push userkwierso@gmail.com
push dateWed, 16 Sep 2015 18:04:49 +0000
treeherderfx-team@e7d613b3bcfe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1204775
milestone43.0a1
Bug 1204775 - SharedWorker.port should be a 'real' MessagePort, r=khuey
dom/base/PostMessageEvent.cpp
dom/base/PostMessageEvent.h
dom/base/StructuredCloneHelper.cpp
dom/base/StructuredCloneHelper.h
dom/bindings/Bindings.conf
dom/events/MessageEvent.cpp
dom/events/MessageEvent.h
dom/messagechannel/MessageChannel.cpp
dom/messagechannel/MessageChannel.h
dom/messagechannel/MessagePort.cpp
dom/messagechannel/MessagePort.h
dom/messagechannel/MessagePortList.h
dom/workers/MessagePort.cpp
dom/workers/MessagePort.h
dom/workers/RuntimeService.cpp
dom/workers/ServiceWorkerClient.cpp
dom/workers/ServiceWorkerRegistrar.cpp
dom/workers/SharedWorker.cpp
dom/workers/SharedWorker.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/moz.build
dom/workers/test/mochitest.ini
dom/workers/test/sharedWorker_ports.js
dom/workers/test/test_sharedWorker.html
dom/workers/test/test_sharedWorker_ports.html
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -109,18 +109,17 @@ PostMessageEvent::Run()
     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;
-  TakeTransferredPorts(ports);
+  nsTArray<nsRefPtr<MessagePort>> ports = TakeTransferredPorts();
 
   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.
--- a/dom/base/PostMessageEvent.h
+++ b/dom/base/PostMessageEvent.h
@@ -15,19 +15,16 @@
 
 class nsGlobalWindow;
 class nsIPrincipal;
 class nsPIDOMWindow;
 
 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 StructuredCloneHelper
 {
 public:
--- a/dom/base/StructuredCloneHelper.cpp
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -1067,26 +1067,24 @@ StructuredCloneHelper::WriteTransferCall
                                              void** aContent,
                                              uint64_t* aExtraData)
 {
   if (!mSupportsTransferring) {
     return false;
   }
 
   {
-    MessagePortBase* port = nullptr;
+    MessagePort* port = nullptr;
     nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
     if (NS_SUCCEEDED(rv)) {
       // We use aExtraData to store the index of this new port identifier.
       *aExtraData = mPortIdentifiers.Length();
       MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement();
 
-      if (!port->CloneAndDisentangle(*identifier)) {
-        return false;
-      }
+      port->CloneAndDisentangle(*identifier);
 
       *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
       *aOwnership = JS::SCTAG_TMO_CUSTOM;
       *aContent = nullptr;
 
       return true;
     }
   }
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -2,16 +2,17 @@
 /* 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_StructuredCloneHelper_h
 #define mozilla_dom_StructuredCloneHelper_h
 
 #include "js/StructuredClone.h"
+#include "mozilla/Move.h"
 #include "nsAutoPtr.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 #ifdef DEBUG
 #include "nsIThread.h"
 #endif
 
@@ -106,17 +107,17 @@ protected:
   nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
 
 #ifdef DEBUG
   bool mShutdownCalled;
 #endif
 };
 
 class BlobImpl;
-class MessagePortBase;
+class MessagePort;
 class MessagePortIdentifier;
 
 class StructuredCloneHelper : public StructuredCloneHelperInternal
 {
 public:
   enum CloningSupport
   {
     CloningSupported,
@@ -186,21 +187,20 @@ public:
   nsISupports* ParentDuringRead() const
   {
     return mParent;
   }
 
   // This must be called if the transferring has ports generated by Read().
   // MessagePorts are not thread-safe and they must be retrieved in the thread
   // where they are created.
-  void TakeTransferredPorts(nsTArray<nsRefPtr<MessagePortBase>>& aPorts)
+  nsTArray<nsRefPtr<MessagePort>>&& TakeTransferredPorts()
   {
     MOZ_ASSERT(mSupportsTransferring);
-    MOZ_ASSERT(aPorts.IsEmpty());
-    aPorts.SwapElements(mTransferredPorts);
+    return Move(mTransferredPorts);
   }
 
   nsTArray<MessagePortIdentifier>& PortIdentifiers()
   {
     MOZ_ASSERT(mSupportsTransferring);
     return mPortIdentifiers;
   }
 
@@ -286,17 +286,17 @@ protected:
   nsTArray<nsRefPtr<layers::Image>> mClonedImages;
 
   // This raw pointer is set and unset into the ::Read(). It's always null
   // outside that method. For this reason it's a raw pointer.
   nsISupports* MOZ_NON_OWNING_REF mParent;
 
   // This array contains the ports once we've finished the reading. It's
   // generated from the mPortIdentifiers array.
-  nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+  nsTArray<nsRefPtr<MessagePort>> mTransferredPorts;
 
   // This array contains the identifiers of the MessagePorts. Based on these we
   // are able to reconnect the new transferred ports with the other
   // MessageChannel ports.
   nsTArray<MessagePortIdentifier> mPortIdentifiers;
 
 #ifdef DEBUG
   nsCOMPtr<nsIThread> mCreationThread;
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -734,21 +734,16 @@ DOMInterfaces = {
 'MediaStreamTrack': {
     'concrete': False
 },
 
 'MediaRecorder': {
     'headerFile': 'MediaRecorder.h',
 },
 
-'MessagePort': {
-    'nativeType': 'mozilla::dom::MessagePortBase',
-    'headerFile': 'mozilla/dom/MessagePort.h',
-},
-
 'MimeType': {
     'headerFile' : 'nsMimeTypeArray.h',
     'nativeType': 'nsMimeType',
 },
 
 'MimeTypeArray': {
     'nativeType': 'nsMimeTypeArray',
 },
--- a/dom/events/MessageEvent.cpp
+++ b/dom/events/MessageEvent.cpp
@@ -162,17 +162,17 @@ MessageEvent::Constructor(EventTarget* a
     } else {
       event->mPortSource = aParam.mSource.Value().GetAsMessagePort();
     }
 
     MOZ_ASSERT(event->mWindowSource || event->mPortSource);
   }
 
   if (aParam.mPorts.WasPassed() && !aParam.mPorts.Value().IsNull()) {
-    nsTArray<nsRefPtr<MessagePortBase>> ports;
+    nsTArray<nsRefPtr<MessagePort>> ports;
     for (uint32_t i = 0, len = aParam.mPorts.Value().Value().Length(); i < len; ++i) {
       ports.AppendElement(aParam.mPorts.Value().Value()[i].get());
     }
 
     event->mPorts = new MessagePortList(static_cast<Event*>(event), ports);
   }
 
   return event.forget();
--- a/dom/events/MessageEvent.h
+++ b/dom/events/MessageEvent.h
@@ -12,17 +12,16 @@
 #include "nsIDOMMessageEvent.h"
 #include "mozilla/dom/MessagePortList.h"
 
 namespace mozilla {
 namespace dom {
 
 struct MessageEventInit;
 class MessagePort;
-class MessagePortBase;
 class MessagePortList;
 class OwningWindowProxyOrMessagePortOrClient;
 
 namespace workers {
 
 class ServiceWorkerClient;
 
 } // namespace workers
@@ -89,17 +88,17 @@ public:
 protected:
   ~MessageEvent();
 
 private:
   JS::Heap<JS::Value> mData;
   nsString mOrigin;
   nsString mLastEventId;
   nsCOMPtr<nsIDOMWindow> mWindowSource;
-  nsRefPtr<MessagePortBase> mPortSource;
+  nsRefPtr<MessagePort> mPortSource;
   nsRefPtr<workers::ServiceWorkerClient> mClientSource;
   nsRefPtr<MessagePortList> mPorts;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 already_AddRefed<mozilla::dom::MessageEvent>
--- a/dom/messagechannel/MessageChannel.cpp
+++ b/dom/messagechannel/MessageChannel.cpp
@@ -44,37 +44,42 @@ MessageChannel::WrapObject(JSContext* aC
   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());
+  return Constructor(window, aRv);
+}
 
+/* static */ already_AddRefed<MessageChannel>
+MessageChannel::Constructor(nsPIDOMWindow* aWindow, ErrorResult& aRv)
+{
   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);
+  nsRefPtr<MessageChannel> channel = new MessageChannel(aWindow);
 
-  channel->mPort1 = MessagePort::Create(window, portUUID1, portUUID2, aRv);
+  channel->mPort1 = MessagePort::Create(aWindow, portUUID1, portUUID2, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  channel->mPort2 = MessagePort::Create(window, portUUID2, portUUID1, aRv);
+  channel->mPort2 = MessagePort::Create(aWindow, portUUID2, portUUID1, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   channel->mPort1->UnshippedEntangle(channel->mPort2);
   channel->mPort2->UnshippedEntangle(channel->mPort1);
 
   return channel.forget();
--- a/dom/messagechannel/MessageChannel.h
+++ b/dom/messagechannel/MessageChannel.h
@@ -35,16 +35,19 @@ public:
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<MessageChannel>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
+  static already_AddRefed<MessageChannel>
+  Constructor(nsPIDOMWindow* aWindow, ErrorResult& aRv);
+
   MessagePort*
   Port1() const
   {
     return mPort1;
   }
 
   MessagePort*
   Port2() const
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -135,18 +135,17 @@ public:
 
     event->InitMessageEvent(NS_LITERAL_STRING("message"),
                             false /* non-bubbling */,
                             false /* cancelable */, value, EmptyString(),
                             EmptyString(), nullptr);
     event->SetTrusted(true);
     event->SetSource(mPort);
 
-    nsTArray<nsRefPtr<MessagePortBase>> ports;
-    mData->TakeTransferredPorts(ports);
+    nsTArray<nsRefPtr<MessagePort>> ports = mData->TakeTransferredPorts();
 
     nsRefPtr<MessagePortList> portList =
       new MessagePortList(static_cast<dom::Event*>(event.get()),
                           ports);
     event->SetPorts(portList);
 
     bool dummy;
     mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
@@ -166,54 +165,45 @@ private:
   {}
 
   nsRefPtr<MessagePort> mPort;
   nsRefPtr<SharedMessagePortMessage> mData;
 };
 
 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,
-                                                MessagePortBase)
+                                                DOMEventTargetHelper)
   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,
-                                                  MessagePortBase)
+                                                  DOMEventTargetHelper)
   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_ENTRY(nsIIPCBackgroundChildCreateCallback)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
-NS_INTERFACE_MAP_END_INHERITING(MessagePortBase)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-NS_IMPL_ADDREF_INHERITED(MessagePort, MessagePortBase)
-NS_IMPL_RELEASE_INHERITED(MessagePort, MessagePortBase)
+NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
 
 namespace {
 
 class MessagePortFeature final : public workers::WorkerFeature
 {
   MessagePort* mPort;
 
 public:
@@ -282,17 +272,17 @@ private:
   const MessagePortIdentifier mIdentifier;
 };
 
 NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
 
 } // namespace
 
 MessagePort::MessagePort(nsPIDOMWindow* aWindow)
-  : MessagePortBase(aWindow)
+  : DOMEventTargetHelper(aWindow)
   , mInnerID(0)
   , mMessageQueueEnabled(false)
   , mIsKeptAlive(false)
 {
   mIdentifier = new MessagePortIdentifier();
   mIdentifier->neutered() = true;
   mIdentifier->sequenceId() = 0;
 }
@@ -359,38 +349,38 @@ MessagePort::Initialize(const nsID& aUUI
     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 {
+  if (!NS_IsMainThread()) {
     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);
+  } else if (GetOwner()) {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(GetOwner()->IsInnerWindow());
+    mInnerID = GetOwner()->WindowID();
+
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->AddObserver(this, "inner-window-destroyed", false);
+    }
   }
 }
 
 JSObject*
 MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MessagePortBinding::Wrap(aCx, this, aGivenProto);
 }
@@ -409,17 +399,17 @@ MessagePort::PostMessage(JSContext* aCx,
 
     // 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;
+      MessagePort* 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;
@@ -515,37 +505,32 @@ MessagePort::Dispatch()
   mDispatchRunnable = new DispatchEventRunnable(this);
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(mDispatchRunnable)));
 }
 
 void
 MessagePort::Close()
 {
-  // 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;
   }
 
   // Not entangled yet, we have to wait.
-  if (mState < eStateEntangling) {
+  if (mState == eStateEntangling) {
     mNextStep = eNextStepClose;
     return;
   }
 
   if (mState > eStateEntangled) {
     return;
   }
 
@@ -694,34 +679,34 @@ MessagePort::Disentangle()
   mActor->SendDisentangle(messages);
 
   mActor->SetPort(nullptr);
   mActor = nullptr;
 
   UpdateMustKeepAlive();
 }
 
-bool
+void
 MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
 {
   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;
+    return;
   }
 
   // We already have a 'next step'. We have to consider this port as already
   // cloned/closed/disentangled.
   if (mNextStep != eNextStepNone) {
-    return true;
+    return;
   }
 
   aIdentifier.uuid() = mIdentifier->uuid();
   aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
   aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
   aIdentifier.neutered() = false;
 
   // We have to entangle first.
@@ -734,34 +719,33 @@ MessagePort::CloneAndDisentangle(Message
     mUnshippedEntangledPort = nullptr;
 
     // 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;
+      return;
     }
 
     // Register this component to PBackground.
     ConnectToPBackground();
 
     mNextStep = eNextStepDisentangle;
-    return true;
+    return;
   }
 
   // Not entangled yet, we have to wait.
   if (mState < eStateEntangled) {
     mNextStep = eNextStepDisentangle;
-    return true;
+    return;
   }
 
   StartDisentangling();
-  return true;
 }
 
 void
 MessagePort::Closed()
 {
   if (mState == eStateDisentangled) {
     return;
   }
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -26,95 +26,61 @@ class MessagePortChild;
 class MessagePortIdentifier;
 class MessagePortMessage;
 class SharedMessagePortMessage;
 
 namespace workers {
 class WorkerFeature;
 } // namespace workers
 
-class MessagePortBase : public DOMEventTargetHelper
-{
-protected:
-  explicit MessagePortBase(nsPIDOMWindow* aWindow);
-  MessagePortBase();
-
-public:
-
-  virtual void
-  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 populates a MessagePortIdentifier object with the information
-  // useful to create new MessagePort.
-  virtual bool
-  CloneAndDisentangle(MessagePortIdentifier& aIdentifier) = 0;
-};
-
-class MessagePort final : public MessagePortBase
+class MessagePort final : public DOMEventTargetHelper
                         , public nsIIPCBackgroundChildCreateCallback
                         , public nsIObserver
 {
   friend class DispatchEventRunnable;
 
 public:
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECL_NSIOBSERVER
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
-                                           MessagePortBase)
+                                           DOMEventTargetHelper)
 
   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);
 
   static void
   ForceClose(const MessagePortIdentifier& aIdentifier);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  virtual void
+  void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Optional<Sequence<JS::Value>>& aTransferable,
-              ErrorResult& aRv) override;
+              ErrorResult& aRv);
 
-  virtual void Start() override;
+  void Start();
 
-  virtual void Close() override;
+  void Close();
 
-  virtual EventHandlerNonNull* GetOnmessage() override;
+  EventHandlerNonNull* GetOnmessage();
 
-  virtual void SetOnmessage(EventHandlerNonNull* aCallback) override;
+  void SetOnmessage(EventHandlerNonNull* aCallback);
 
   // Non WebIDL methods
 
   void UnshippedEntangle(MessagePort* aEntangledPort);
 
-  virtual bool CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
+  void CloneAndDisentangle(MessagePortIdentifier& aIdentifier);
 
   // These methods are useful for MessagePortChild
 
   void Entangled(nsTArray<MessagePortMessage>& aMessages);
   void MessagesReceived(nsTArray<MessagePortMessage>& aMessages);
   void StopSendingDataConfirmed();
   void Closed();
 
--- a/dom/messagechannel/MessagePortList.h
+++ b/dom/messagechannel/MessagePortList.h
@@ -24,17 +24,17 @@ class MessagePortList final : public nsI
   ~MessagePortList() {}
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MessagePortList)
 
 public:
   MessagePortList(nsISupports* aOwner,
-                  const nsTArray<nsRefPtr<MessagePortBase>>& aPorts)
+                  const nsTArray<nsRefPtr<MessagePort>>& aPorts)
     : mOwner(aOwner)
     , mPorts(aPorts)
   {
   }
 
   nsISupports*
   GetParentObject() const
   {
@@ -45,34 +45,34 @@ public:
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   uint32_t
   Length() const
   {
     return mPorts.Length();
   }
 
-  MessagePortBase*
+  MessagePort*
   Item(uint32_t aIndex)
   {
     return mPorts.SafeElementAt(aIndex);
   }
 
-  MessagePortBase*
+  MessagePort*
   IndexedGetter(uint32_t aIndex, bool &aFound)
   {
     aFound = aIndex < mPorts.Length();
     if (!aFound) {
       return nullptr;
     }
     return mPorts[aIndex];
   }
 
 public:
   nsCOMPtr<nsISupports> mOwner;
-  nsTArray<nsRefPtr<MessagePortBase>> mPorts;
+  nsTArray<nsRefPtr<MessagePort>> mPorts;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MessagePortList_h
 
deleted file mode 100644
--- a/dom/workers/MessagePort.cpp
+++ /dev/null
@@ -1,318 +0,0 @@
-/* -*- 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 "mozilla/EventDispatcher.h"
-#include "mozilla/dom/MessagePortBinding.h"
-#include "mozilla/dom/ScriptSettings.h"
-#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 {
-
-class DelayedEventRunnable final : public WorkerRunnable
-{
-  nsRefPtr<mozilla::dom::workers::MessagePort> mMessagePort;
-  nsTArray<nsCOMPtr<nsIDOMEvent>> mEvents;
-
-public:
-  DelayedEventRunnable(WorkerPrivate* aWorkerPrivate,
-                       TargetAndBusyBehavior aBehavior,
-                       mozilla::dom::workers::MessagePort* aMessagePort,
-                       nsTArray<nsCOMPtr<nsIDOMEvent>>& aEvents)
-  : WorkerRunnable(aWorkerPrivate, aBehavior), mMessagePort(aMessagePort)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aMessagePort);
-    MOZ_ASSERT(aEvents.Length());
-
-    mEvents.SwapElements(aEvents);
-  }
-
-  bool PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-  {
-    if (mBehavior == WorkerThreadModifyBusyCount) {
-      return aWorkerPrivate->ModifyBusyCount(aCx, true);
-    }
-
-    return true;
-  }
-
-  void PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-                    bool aDispatchResult)
-  {
-    if (!aDispatchResult) {
-      if (mBehavior == WorkerThreadModifyBusyCount) {
-        aWorkerPrivate->ModifyBusyCount(aCx, false);
-      }
-      if (aCx) {
-        JS_ReportPendingException(aCx);
-      }
-    }
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
-};
-
-} // namespace
-
-BEGIN_WORKERS_NAMESPACE
-
-MessagePort::MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
-                         uint64_t aSerial)
-: MessagePortBase(aWindow), mSharedWorker(aSharedWorker),
-  mWorkerPrivate(nullptr), mSerial(aSerial), mStarted(false)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aSharedWorker);
-}
-
-MessagePort::MessagePort(WorkerPrivate* aWorkerPrivate, uint64_t aSerial)
-: mWorkerPrivate(aWorkerPrivate), mSerial(aSerial), mStarted(false)
-{
-  aWorkerPrivate->AssertIsOnWorkerThread();
-}
-
-MessagePort::~MessagePort()
-{
-  Close();
-}
-
-void
-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;
-  }
-
-  if (mSharedWorker) {
-    mSharedWorker->PostMessage(aCx, aMessage, aTransferable, aRv);
-  }
-  else {
-    mWorkerPrivate->PostMessageToParentMessagePort(aCx, Serial(), aMessage,
-                                                   aTransferable, aRv);
-  }
-}
-
-void
-MessagePort::Start()
-{
-  AssertCorrectThread();
-
-  if (IsClosed()) {
-    NS_WARNING("Called start() after calling close()!");
-    return;
-  }
-
-  if (mStarted) {
-    return;
-  }
-
-  mStarted = true;
-
-  if (!mQueuedEvents.IsEmpty()) {
-    WorkerPrivate* workerPrivate;
-    WorkerRunnable::TargetAndBusyBehavior behavior;
-
-    if (mWorkerPrivate) {
-      workerPrivate = mWorkerPrivate;
-      behavior = WorkerRunnable::WorkerThreadModifyBusyCount;
-    }
-    else {
-      workerPrivate = mSharedWorker->GetWorkerPrivate();
-      MOZ_ASSERT(workerPrivate);
-
-      behavior = WorkerRunnable::ParentThreadUnchangedBusyCount;
-    }
-
-    nsRefPtr<DelayedEventRunnable> runnable =
-      new DelayedEventRunnable(workerPrivate, behavior, this, mQueuedEvents);
-    runnable->Dispatch(nullptr);
-  }
-}
-
-void
-MessagePort::Close()
-{
-  AssertCorrectThread();
-
-  if (!IsClosed()) {
-    CloseInternal();
-  }
-}
-
-void
-MessagePort::QueueEvent(nsIDOMEvent* aEvent)
-{
-  AssertCorrectThread();
-  MOZ_ASSERT(aEvent);
-  MOZ_ASSERT(!IsClosed());
-  MOZ_ASSERT(!mStarted);
-
-  mQueuedEvents.AppendElement(aEvent);
-}
-
-EventHandlerNonNull*
-MessagePort::GetOnmessage()
-{
-  AssertCorrectThread();
-
-  return NS_IsMainThread() ? GetEventHandler(nsGkAtoms::onmessage, EmptyString())
-                           : GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
-}
-
-void
-MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
-{
-  AssertCorrectThread();
-
-  if (NS_IsMainThread()) {
-    SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
-  }
-  else {
-    SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
-  }
-
-  Start();
-}
-
-bool
-MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
-{
-  NS_WARNING("Haven't implemented structured clone for these ports yet!");
-  return false;
-}
-
-void
-MessagePort::CloseInternal()
-{
-  AssertCorrectThread();
-  MOZ_ASSERT(!IsClosed());
-  MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty());
-
-  if (!mStarted) {
-    mQueuedEvents.Clear();
-  }
-
-  mSharedWorker = nullptr;
-  mWorkerPrivate = nullptr;
-}
-
-#ifdef DEBUG
-void
-MessagePort::AssertCorrectThread() const
-{
-  if (IsClosed()) {
-    return; // Can't assert anything if we nulled out our pointers.
-  }
-
-  MOZ_ASSERT((mSharedWorker || mWorkerPrivate) &&
-             !(mSharedWorker && mWorkerPrivate));
-
-  if (mSharedWorker) {
-    AssertIsOnMainThread();
-  }
-  else {
-    mWorkerPrivate->AssertIsOnWorkerThread();
-  }
-}
-#endif
-
-NS_IMPL_ADDREF_INHERITED(mozilla::dom::workers::MessagePort, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(mozilla::dom::workers::MessagePort, DOMEventTargetHelper)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
-NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
-                                                  DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorker)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEvents)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
-                                                DOMEventTargetHelper)
-  tmp->Close();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-JSObject*
-MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  AssertCorrectThread();
-
-  return MessagePortBinding::Wrap(aCx, this, aGivenProto);
-}
-
-nsresult
-MessagePort::PreHandleEvent(EventChainPreVisitor& aVisitor)
-{
-  AssertCorrectThread();
-
-  nsIDOMEvent*& event = aVisitor.mDOMEvent;
-
-  if (event) {
-    bool preventDispatch = false;
-
-    if (IsClosed()) {
-      preventDispatch = true;
-    } else if (NS_IsMainThread() && mSharedWorker->IsFrozen()) {
-      mSharedWorker->QueueEvent(event);
-      preventDispatch = true;
-    } else if (!mStarted) {
-      QueueEvent(event);
-      preventDispatch = true;
-    }
-
-    if (preventDispatch) {
-      aVisitor.mCanHandle = false;
-      aVisitor.mParentTarget = nullptr;
-      return NS_OK;
-    }
-  }
-
-  return DOMEventTargetHelper::PreHandleEvent(aVisitor);
-}
-
-END_WORKERS_NAMESPACE
-
-bool
-DelayedEventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-{
-  MOZ_ASSERT(mMessagePort);
-  mMessagePort->AssertCorrectThread();
-  MOZ_ASSERT(mEvents.Length());
-
-  AutoNoJSAPI nojsapi;
-
-  bool ignored;
-  for (uint32_t i = 0; i < mEvents.Length(); i++) {
-    mMessagePort->DispatchEvent(mEvents[i], &ignored);
-  }
-
-  return true;
-}
deleted file mode 100644
--- a/dom/workers/MessagePort.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/* -*- 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_workers_messageport_h_
-#define mozilla_dom_workers_messageport_h_
-
-#include "mozilla/dom/workers/Workers.h"
-
-#include "mozilla/DOMEventTargetHelper.h"
-#include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/MessagePort.h"
-
-class nsIDOMEvent;
-class nsPIDOMWindow;
-
-namespace mozilla {
-class EventChainPreVisitor;
-} // namespace mozilla
-
-BEGIN_WORKERS_NAMESPACE
-
-class SharedWorker;
-class WorkerPrivate;
-
-class MessagePort final : public mozilla::dom::MessagePortBase
-{
-  friend class SharedWorker;
-  friend class WorkerPrivate;
-
-  typedef mozilla::ErrorResult ErrorResult;
-
-  nsRefPtr<SharedWorker> mSharedWorker;
-  WorkerPrivate* mWorkerPrivate;
-  nsTArray<nsCOMPtr<nsIDOMEvent>> mQueuedEvents;
-  uint64_t mSerial;
-  bool mStarted;
-
-public:
-  static bool
-  PrefEnabled();
-
-  virtual void
-  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
-  Serial() const
-  {
-    return mSerial;
-  }
-
-  void
-  QueueEvent(nsIDOMEvent* aEvent);
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort, DOMEventTargetHelper)
-
-  virtual EventHandlerNonNull*
-  GetOnmessage() override;
-
-  virtual void
-  SetOnmessage(EventHandlerNonNull* aCallback) override;
-
-  virtual bool
-  CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
-
-  bool
-  IsClosed() const
-  {
-    return !mSharedWorker && !mWorkerPrivate;
-  }
-
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  virtual nsresult
-  PreHandleEvent(EventChainPreVisitor& aVisitor) override;
-
-#ifdef DEBUG
-  void
-  AssertCorrectThread() const;
-#else
-  void
-  AssertCorrectThread() const { }
-#endif
-
-private:
-  // This class can only be created by SharedWorker or WorkerPrivate.
-  MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
-              uint64_t aSerial);
-  MessagePort(WorkerPrivate* aWorkerPrivate, uint64_t aSerial);
-
-  // This class is reference-counted and will be destroyed from Release().
-  ~MessagePort();
-
-  void
-  CloseInternal();
-};
-
-END_WORKERS_NAMESPACE
-
-#endif // mozilla_dom_workers_messageport_h_
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/AtomList.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
+#include "mozilla/dom/MessageChannel.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Navigator.h"
@@ -2488,34 +2489,43 @@ RuntimeService::CreateSharedWorkerFromLo
 
   // Keep a reference to the window before spawning the worker. If the worker is
   // a Shared/Service worker and the worker script loads and executes before
   // the SharedWorker object itself is created before then WorkerScriptLoaded()
   // will reset the loadInfo's window.
   nsCOMPtr<nsPIDOMWindow> window = aLoadInfo->mWindow;
 
   bool created = false;
+  ErrorResult rv;
   if (!workerPrivate) {
-    ErrorResult rv;
     workerPrivate =
       WorkerPrivate::Constructor(aCx, aScriptURL, false,
                                  aType, aName, aLoadInfo, rv);
     NS_ENSURE_TRUE(workerPrivate, rv.StealNSResult());
 
     created = true;
   } else {
     // If we're attaching to an existing SharedWorker private, then we
     // must update the overriden load group to account for our document's
     // load group.
     workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup);
   }
 
-  nsRefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate);
-
-  if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker)) {
+  // We don't actually care about this MessageChannel, but we use it to 'steal'
+  // its 2 connected ports.
+  nsRefPtr<MessageChannel> channel = MessageChannel::Constructor(window, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
+  nsRefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate,
+                                                         channel->Port1());
+
+  if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker,
+                                           channel->Port2())) {
     NS_WARNING("Worker is unreachable, this shouldn't happen!");
     sharedWorker->Close();
     return NS_ERROR_FAILURE;
   }
 
   // This is normally handled in RegisterWorker, but that wasn't called if the
   // worker already existed.
   if (!created) {
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -133,18 +133,17 @@ private:
                                  EmptyString(),
                                  EmptyString(),
                                  nullptr);
     if (NS_WARN_IF(rv.Failed())) {
       xpc::Throw(aCx, rv.StealNSResult());
       return NS_ERROR_FAILURE;
     }
 
-    nsTArray<nsRefPtr<MessagePortBase>> ports;
-    TakeTransferredPorts(ports);
+    nsTArray<nsRefPtr<MessagePort>> ports = TakeTransferredPorts();
 
     nsRefPtr<MessagePortList> portList =
       new MessagePortList(static_cast<dom::Event*>(event.get()),
                           ports);
     event->SetPorts(portList);
 
     event->SetTrusted(true);
     bool status = false;
--- a/dom/workers/ServiceWorkerRegistrar.cpp
+++ b/dom/workers/ServiceWorkerRegistrar.cpp
@@ -6,16 +6,17 @@
 
 #include "ServiceWorkerRegistrar.h"
 #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
 
 #include "nsIEventTarget.h"
 #include "nsIInputStream.h"
 #include "nsILineInputStream.h"
 #include "nsIObserverService.h"
+#include "nsIOutputStream.h"
 #include "nsISafeOutputStream.h"
 
 #include "MainThreadUtils.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ModuleUtils.h"
--- a/dom/workers/SharedWorker.cpp
+++ b/dom/workers/SharedWorker.cpp
@@ -5,42 +5,40 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SharedWorker.h"
 
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/SharedWorkerBinding.h"
 #include "nsContentUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIDOMEvent.h"
 
-#include "MessagePort.h"
 #include "RuntimeService.h"
 #include "WorkerPrivate.h"
 
 using mozilla::dom::Optional;
 using mozilla::dom::Sequence;
 using namespace mozilla;
 
 USING_WORKERS_NAMESPACE
 
 SharedWorker::SharedWorker(nsPIDOMWindow* aWindow,
-                           WorkerPrivate* aWorkerPrivate)
-: DOMEventTargetHelper(aWindow), mWorkerPrivate(aWorkerPrivate),
-  mFrozen(false)
+                           WorkerPrivate* aWorkerPrivate,
+                           MessagePort* aMessagePort)
+: DOMEventTargetHelper(aWindow), mWorkerPrivate(aWorkerPrivate)
+, mMessagePort(aMessagePort)
+, mFrozen(false)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWorkerPrivate);
-
-  mSerial = aWorkerPrivate->NextMessagePortSerial();
-
-  mMessagePort = new MessagePort(aWindow, this, mSerial);
 }
 
 SharedWorker::~SharedWorker()
 {
   AssertIsOnMainThread();
   Close();
   MOZ_ASSERT(!mWorkerPrivate);
 }
@@ -71,23 +69,21 @@ SharedWorker::Constructor(const GlobalOb
   if (NS_FAILED(rv)) {
     aRv = rv;
     return nullptr;
   }
 
   return sharedWorker.forget();
 }
 
-already_AddRefed<mozilla::dom::workers::MessagePort>
+MessagePort*
 SharedWorker::Port()
 {
   AssertIsOnMainThread();
-
-  nsRefPtr<MessagePort> messagePort = mMessagePort;
-  return messagePort.forget();
+  return mMessagePort;
 }
 
 void
 SharedWorker::Freeze()
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(!IsFrozen());
 
@@ -152,18 +148,17 @@ void
 SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                           const Optional<Sequence<JS::Value>>& aTransferable,
                           ErrorResult& aRv)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mWorkerPrivate);
   MOZ_ASSERT(mMessagePort);
 
-  mWorkerPrivate->PostMessageToMessagePort(aCx, mMessagePort->Serial(),
-                                           aMessage, aTransferable, aRv);
+  mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv);
 }
 
 void
 SharedWorker::NoteDeadWorker(JSContext* aCx)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mWorkerPrivate);
 
--- a/dom/workers/SharedWorker.h
+++ b/dom/workers/SharedWorker.h
@@ -5,61 +5,55 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_sharedworker_h__
 #define mozilla_dom_workers_sharedworker_h__
 
 #include "Workers.h"
 
 #include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/workers/bindings/MessagePort.h"
 #include "mozilla/DOMEventTargetHelper.h"
 
 class nsIDOMEvent;
 class nsPIDOMWindow;
 
 namespace mozilla {
 class EventChainPreVisitor;
+
+namespace dom {
+class MessagePort;
+}
 } // namespace mozilla
 
 BEGIN_WORKERS_NAMESPACE
 
-class MessagePort;
 class RuntimeService;
 class WorkerPrivate;
 
 class SharedWorker final : public DOMEventTargetHelper
 {
-  friend class MessagePort;
   friend class RuntimeService;
 
   typedef mozilla::ErrorResult ErrorResult;
   typedef mozilla::dom::GlobalObject GlobalObject;
 
   nsRefPtr<WorkerPrivate> mWorkerPrivate;
   nsRefPtr<MessagePort> mMessagePort;
   nsTArray<nsCOMPtr<nsIDOMEvent>> mFrozenEvents;
-  uint64_t mSerial;
   bool mFrozen;
 
 public:
   static already_AddRefed<SharedWorker>
   Constructor(const GlobalObject& aGlobal, JSContext* aCx,
               const nsAString& aScriptURL, const Optional<nsAString>& aName,
               ErrorResult& aRv);
 
-  already_AddRefed<mozilla::dom::workers::MessagePort>
+  MessagePort*
   Port();
 
-  uint64_t
-  Serial() const
-  {
-    return mSerial;
-  }
-
   bool
   IsFrozen() const
   {
     return mFrozen;
   }
 
   void
   Freeze();
@@ -88,17 +82,18 @@ public:
   GetWorkerPrivate() const
   {
     return mWorkerPrivate;
   }
 
 private:
   // This class can only be created from the RuntimeService.
   SharedWorker(nsPIDOMWindow* aWindow,
-               WorkerPrivate* aWorkerPrivate);
+               WorkerPrivate* aWorkerPrivate,
+               MessagePort* aMessagePort);
 
   // This class is reference-counted and will be destroyed from Release().
   ~SharedWorker();
 
   // Only called by MessagePort.
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Optional<Sequence<JS::Value>>& aTransferable,
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -49,16 +49,17 @@
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/FunctionBinding.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/PMessagePort.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseDebugging.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
 #include "mozilla/dom/WorkerGlobalScopeBinding.h"
@@ -83,17 +84,16 @@
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 #ifdef DEBUG
 #include "nsThreadManager.h"
 #endif
 
-#include "MessagePort.h"
 #include "Navigator.h"
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "ServiceWorkerManager.h"
 #include "ServiceWorkerWindowClient.h"
 #include "SharedWorker.h"
 #include "WorkerDebuggerManager.h"
@@ -589,31 +589,25 @@ private:
 
     aWorkerPrivate->CloseHandlerFinished();
   }
 };
 
 class MessageEventRunnable final : public WorkerRunnable
                                  , public StructuredCloneHelper
 {
-  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,
-                       bool aToMessagePort, uint64_t aMessagePortSerial)
+                       TargetAndBusyBehavior aBehavior)
   : WorkerRunnable(aWorkerPrivate, aBehavior)
   , StructuredCloneHelper(CloningSupported, TransferringSupported,
                           SameProcessDifferentThread)
-  , mMessagePortSerial(aMessagePortSerial)
-  , mToMessagePort(aToMessagePort)
   {
   }
 
   void
   SetMessageSource(ServiceWorkerClientInfo* aSource)
   {
     mEventSource = aSource;
   }
@@ -649,72 +643,52 @@ public:
       event->SetSource(client);
     }
 
     if (NS_WARN_IF(rv.Failed())) {
       xpc::Throw(aCx, rv.StealNSResult());
       return false;
     }
 
-    nsTArray<nsRefPtr<MessagePortBase>> ports;
-    TakeTransferredPorts(ports);
+    nsTArray<nsRefPtr<MessagePort>> ports = TakeTransferredPorts();
 
     event->SetTrusted(true);
     event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
                                         ports));
     nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
 
     nsEventStatus dummy = nsEventStatus_eIgnore;
     aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
     return true;
   }
 
 private:
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
-    MOZ_ASSERT_IF(mToMessagePort, aWorkerPrivate->IsSharedWorker());
-
     if (mBehavior == ParentThreadUnchangedBusyCount) {
       // Don't fire this event if the JS object has been disconnected from the
       // private object.
       if (!aWorkerPrivate->IsAcceptingEvents()) {
         return true;
       }
 
-      if (mToMessagePort) {
-        return
-          aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
-                                                            mMessagePortSerial,
-                                                            *this);
-      }
-
       if (aWorkerPrivate->IsFrozen()) {
         aWorkerPrivate->QueueRunnable(this);
         return true;
       }
 
       aWorkerPrivate->AssertInnerWindowIsCorrect();
 
       return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate,
                               !aWorkerPrivate->GetParent());
     }
 
     MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
 
-    if (mToMessagePort) {
-      nsRefPtr<workers::MessagePort> port =
-        aWorkerPrivate->GetMessagePort(mMessagePortSerial);
-      if (!port) {
-        // Must have been closed already.
-        return true;
-      }
-      return DispatchDOMEvent(aCx, aWorkerPrivate, port, false);
-    }
-
     return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(),
                             false);
   }
 };
 
 class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable {
   nsString mMessage;
 
@@ -1516,42 +1490,38 @@ public:
     extras->location = nullptr;
 
     aCompartmentStats->extra = extras;
   }
 };
 
 class MessagePortRunnable final : public WorkerRunnable
 {
-  uint64_t mMessagePortSerial;
-  bool mConnect;
+  MessagePortIdentifier mPortIdentifier;
 
 public:
   MessagePortRunnable(WorkerPrivate* aWorkerPrivate,
-                      uint64_t aMessagePortSerial,
-                      bool aConnect)
-  : WorkerRunnable(aWorkerPrivate, aConnect ?
-                                   WorkerThreadModifyBusyCount :
-                                   WorkerThreadUnchangedBusyCount),
-    mMessagePortSerial(aMessagePortSerial), mConnect(aConnect)
-  { }
+                      MessagePort* aPort)
+  : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+  {
+    MOZ_ASSERT(aPort);
+    // In order to move the port from one thread to another one, we have to
+    // close and disentangle it. The output will be a MessagePortIdentifier that
+    // will be used to recreate a new MessagePort on the other thread.
+    aPort->CloneAndDisentangle(mPortIdentifier);
+  }
 
 private:
   ~MessagePortRunnable()
   { }
 
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
-    if (mConnect) {
-      return aWorkerPrivate->ConnectMessagePort(aCx, mMessagePortSerial);
-    }
-
-    aWorkerPrivate->DisconnectMessagePort(mMessagePortSerial);
-    return true;
+    return aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier);
   }
 };
 
 class DummyRunnable final
   : public WorkerRunnable
 {
 public:
   explicit
@@ -2117,18 +2087,17 @@ WorkerPrivateParent<Derived>::WorkerPriv
                                            WorkerType aWorkerType,
                                            const nsACString& aSharedWorkerName,
                                            WorkerLoadInfo& aLoadInfo)
 : mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
   mParent(aParent), mScriptURL(aScriptURL),
   mSharedWorkerName(aSharedWorkerName), mLoadingWorkerScript(false),
-  mBusyCount(0), mMessagePortSerial(0),
-  mParentStatus(Pending), mParentFrozen(false),
+  mBusyCount(0), mParentStatus(Pending), mParentFrozen(false),
   mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
   mWorkerType(aWorkerType),
   mCreationTimeStamp(TimeStamp::Now()),
   mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
 {
   MOZ_ASSERT_IF(!IsDedicatedWorker(),
                 !aSharedWorkerName.IsVoid() && NS_IsMainThread());
   MOZ_ASSERT_IF(IsDedicatedWorker(), aSharedWorkerName.IsEmpty());
@@ -2482,65 +2451,39 @@ bool
 WorkerPrivateParent<Derived>::Freeze(JSContext* aCx, nsPIDOMWindow* aWindow)
 {
   AssertIsOnParentThread();
   MOZ_ASSERT(aCx);
 
   // Shared workers are only frozen if all of their owning documents are
   // frozen. It can happen that mSharedWorkers is empty but this thread has
   // not been unregistered yet.
-  if ((IsSharedWorker() || IsServiceWorker()) && mSharedWorkers.Count()) {
+  if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
     AssertIsOnMainThread();
 
-    struct Closure
-    {
-      nsPIDOMWindow* mWindow;
-      bool mAllFrozen;
-
-      explicit Closure(nsPIDOMWindow* aWindow)
-      : mWindow(aWindow), mAllFrozen(true)
-      {
-        AssertIsOnMainThread();
-        // aWindow may be null here.
+    bool allFrozen = false;
+
+    for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
+      if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
+        // Calling Freeze() may change the refcount, ensure that the worker
+        // outlives this call.
+        nsRefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
+
+        mSharedWorkers[i]->Freeze();
+      } else {
+        MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
+                      !SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
+                                       aWindow));
+        if (!mSharedWorkers[i]->IsFrozen()) {
+          allFrozen = false;
+        }
       }
-
-      static PLDHashOperator
-      Freeze(const uint64_t& aKey,
-              SharedWorker* aSharedWorker,
-              void* aClosure)
-      {
-        AssertIsOnMainThread();
-        MOZ_ASSERT(aSharedWorker);
-        MOZ_ASSERT(aClosure);
-
-        auto closure = static_cast<Closure*>(aClosure);
-
-        if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
-          // Calling Freeze() may change the refcount, ensure that the worker
-          // outlives this call.
-          nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
-
-          aSharedWorker->Freeze();
-        } else {
-          MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
-                        !SameCOMIdentity(aSharedWorker->GetOwner(),
-                                         closure->mWindow));
-          if (!aSharedWorker->IsFrozen()) {
-            closure->mAllFrozen = false;
-          }
-        }
-        return PL_DHASH_NEXT;
-      }
-    };
-
-    Closure closure(aWindow);
-
-    mSharedWorkers.EnumerateRead(Closure::Freeze, &closure);
-
-    if (!closure.mAllFrozen || mParentFrozen) {
+    }
+
+    if (!allFrozen || mParentFrozen) {
       return true;
     }
   }
 
   mParentFrozen = true;
 
   {
     MutexAutoLock lock(mMutex);
@@ -2571,66 +2514,40 @@ WorkerPrivateParent<Derived>::Thaw(JSCon
     // parent was actually suspended (maybe during a sync XHR), and in this case
     // we don't need to thaw.
     return true;
   }
 
   // Shared workers are resumed if any of their owning documents are thawed.
   // It can happen that mSharedWorkers is empty but this thread has not been
   // unregistered yet.
-  if ((IsSharedWorker() || IsServiceWorker()) && mSharedWorkers.Count()) {
+  if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
     AssertIsOnMainThread();
 
-    struct Closure
-    {
-      nsPIDOMWindow* mWindow;
-      bool mAnyRunning;
-
-      explicit Closure(nsPIDOMWindow* aWindow)
-      : mWindow(aWindow), mAnyRunning(false)
-      {
-        AssertIsOnMainThread();
-        // aWindow may be null here.
+    bool anyRunning = false;
+
+    for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
+      if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
+        // Calling Thaw() may change the refcount, ensure that the worker
+        // outlives this call.
+        nsRefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
+
+        mSharedWorkers[i]->Thaw();
+        anyRunning = true;
+      } else {
+        MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
+                      !SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
+                                       aWindow));
+        if (!mSharedWorkers[i]->IsFrozen()) {
+          anyRunning = true;
+        }
       }
-
-      static PLDHashOperator
-      Thaw(const uint64_t& aKey,
-              SharedWorker* aSharedWorker,
-              void* aClosure)
-      {
-        AssertIsOnMainThread();
-        MOZ_ASSERT(aSharedWorker);
-        MOZ_ASSERT(aClosure);
-
-        auto closure = static_cast<Closure*>(aClosure);
-
-        if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
-          // Calling Thaw() may change the refcount, ensure that the worker
-          // outlives this call.
-          nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
-
-          aSharedWorker->Thaw();
-          closure->mAnyRunning = true;
-        } else {
-          MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
-                        !SameCOMIdentity(aSharedWorker->GetOwner(),
-                                         closure->mWindow));
-          if (!aSharedWorker->IsFrozen()) {
-            closure->mAnyRunning = true;
-          }
-        }
-        return PL_DHASH_NEXT;
-      }
-    };
-
-    Closure closure(aWindow);
-
-    mSharedWorkers.EnumerateRead(Closure::Thaw, &closure);
-
-    if (!closure.mAnyRunning || !mParentFrozen) {
+    }
+
+    if (!anyRunning || !mParentFrozen) {
       return true;
     }
   }
 
   MOZ_ASSERT(mParentFrozen);
 
   mParentFrozen = false;
 
@@ -2757,18 +2674,16 @@ WorkerPrivateParent<Derived>::ForgetMain
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::PostMessageInternal(
                                             JSContext* aCx,
                                             JS::Handle<JS::Value> aMessage,
                                             const Optional<Sequence<JS::Value>>& aTransferable,
-                                            bool aToMessagePort,
-                                            uint64_t aMessagePortSerial,
                                             ServiceWorkerClientInfo* aClientInfo,
                                             ErrorResult& aRv)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     if (mParentStatus > Running) {
@@ -2792,18 +2707,17 @@ WorkerPrivateParent<Derived>::PostMessag
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     transferable.setObject(*array);
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(ParentAsWorkerPrivate(),
-                             WorkerRunnable::WorkerThreadModifyBusyCount,
-                             aToMessagePort, aMessagePortSerial);
+                             WorkerRunnable::WorkerThreadModifyBusyCount);
 
   runnable->Write(aCx, aMessage, transferable, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   runnable->SetMessageSource(aClientInfo);
 
@@ -2816,98 +2730,17 @@ template <class Derived>
 void
 WorkerPrivateParent<Derived>::PostMessageToServiceWorker(
                              JSContext* aCx, JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
                              nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
                              ErrorResult& aRv)
 {
   AssertIsOnMainThread();
-  PostMessageInternal(aCx, aMessage, aTransferable, false, 0,
-                      aClientInfo.forget(), aRv);
-}
-
-template <class Derived>
-void
-WorkerPrivateParent<Derived>::PostMessageToMessagePort(
-                             JSContext* aCx,
-                             uint64_t aMessagePortSerial,
-                             JS::Handle<JS::Value> aMessage,
-                             const Optional<Sequence<JS::Value>>& aTransferable,
-                             ErrorResult& aRv)
-{
-  AssertIsOnMainThread();
-
-  PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial,
-                      nullptr, aRv);
-}
-
-template <class Derived>
-bool
-WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
-                                JSContext* aCx, uint64_t aMessagePortSerial,
-                                StructuredCloneHelper& aHelper)
-{
-  AssertIsOnMainThread();
-
-  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;
-  }
-
-  nsCOMPtr<nsISupports> parent = do_QueryInterface(port->GetParentObject());
-
-  AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(port->GetParentObject()))) {
-    return false;
-  }
-  JSContext* cx = jsapi.cx();
-
-  ErrorResult rv;
-  JS::Rooted<JS::Value> data(cx);
-  aHelper.Read(parent, cx, &data, rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    xpc::Throw(cx, rv.StealNSResult());
-    return false;
-  }
-
-  nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr);
-  rv = event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
-                               EmptyString(), EmptyString(), nullptr);
-  if (NS_WARN_IF(rv.Failed())) {
-    xpc::Throw(cx, rv.StealNSResult());
-    return false;
-  }
-
-  nsTArray<nsRefPtr<MessagePortBase>> ports;
-  aHelper.TakeTransferredPorts(ports);
-
-  event->SetTrusted(true);
-  event->SetPorts(new MessagePortList(port, ports));
-
-  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_WARN_IF(rv.Failed())) {
-    xpc::Throw(cx, rv.StealNSResult());
-    return false;
-  }
-
-  return true;
+  PostMessageInternal(aCx, aMessage, aTransferable, aClientInfo.forget(), aRv);
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdateRuntimeOptions(
                                     JSContext* aCx,
                                     const JS::RuntimeOptions& aRuntimeOptions)
 {
@@ -3085,67 +2918,60 @@ WorkerPrivate::OfflineStatusChangeEventI
   event->SetTrusted(true);
 
   globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
-                                                   SharedWorker* aSharedWorker)
+                                                   SharedWorker* aSharedWorker,
+                                                   MessagePort* aPort)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aSharedWorker);
   MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
-  MOZ_ASSERT(!mSharedWorkers.Get(aSharedWorker->Serial()));
+  MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
 
   if (IsSharedWorker()) {
     nsRefPtr<MessagePortRunnable> runnable =
-      new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
-                              true);
+      new MessagePortRunnable(ParentAsWorkerPrivate(), aPort);
     if (!runnable->Dispatch(aCx)) {
       return false;
     }
   }
 
-  mSharedWorkers.Put(aSharedWorker->Serial(), aSharedWorker);
+  mSharedWorkers.AppendElement(aSharedWorker);
 
   // If there were other SharedWorker objects attached to this worker then they
   // may all have been frozen and this worker would need to be thawed.
-  if (mSharedWorkers.Count() > 1 && !Thaw(aCx, nullptr)) {
+  if (mSharedWorkers.Length() > 1 && !Thaw(aCx, nullptr)) {
     return false;
   }
 
   return true;
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UnregisterSharedWorker(
                                                     JSContext* aCx,
                                                     SharedWorker* aSharedWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aSharedWorker);
   MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
-  MOZ_ASSERT(mSharedWorkers.Get(aSharedWorker->Serial()));
-
-  nsRefPtr<MessagePortRunnable> runnable =
-    new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
-                            false);
-  if (!runnable->Dispatch(aCx)) {
-    JS_ReportPendingException(aCx);
-  }
-
-  mSharedWorkers.Remove(aSharedWorker->Serial());
+  MOZ_ASSERT(mSharedWorkers.Contains(aSharedWorker));
+
+  mSharedWorkers.RemoveElement(aSharedWorker);
 
   // If there are still SharedWorker objects attached to this worker then they
   // may all be frozen and this worker would need to be frozen. Otherwise,
   // if that was the last SharedWorker then it's time to cancel this worker.
-  if (mSharedWorkers.Count()) {
+  if (!mSharedWorkers.IsEmpty()) {
     if (!Freeze(aCx, nullptr)) {
       JS_ReportPendingException(aCx);
     }
   } else if (!Cancel(aCx)) {
     JS_ReportPendingException(aCx);
   }
 }
 
@@ -3273,91 +3099,47 @@ WorkerPrivateParent<Derived>::BroadcastE
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::GetAllSharedWorkers(
                                nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
 
-  struct Helper
-  {
-    static PLDHashOperator
-    Collect(const uint64_t& aKey,
-            SharedWorker* aSharedWorker,
-            void* aClosure)
-    {
-      AssertIsOnMainThread();
-      MOZ_ASSERT(aSharedWorker);
-      MOZ_ASSERT(aClosure);
-
-      auto array = static_cast<nsTArray<nsRefPtr<SharedWorker>>*>(aClosure);
-      array->AppendElement(aSharedWorker);
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
   if (!aSharedWorkers.IsEmpty()) {
     aSharedWorkers.Clear();
   }
 
-  mSharedWorkers.EnumerateRead(Helper::Collect, &aSharedWorkers);
+  for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
+    aSharedWorkers.AppendElement(mSharedWorkers[i]);
+  }
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
                                                          nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
   MOZ_ASSERT(aWindow);
 
-  struct Closure
-  {
-    nsPIDOMWindow* mWindow;
-    nsAutoTArray<nsRefPtr<SharedWorker>, 10> mSharedWorkers;
-
-    explicit Closure(nsPIDOMWindow* aWindow)
-    : mWindow(aWindow)
-    {
-      AssertIsOnMainThread();
-      MOZ_ASSERT(aWindow);
-    }
-
-    static PLDHashOperator
-    Collect(const uint64_t& aKey,
-            SharedWorker* aSharedWorker,
-            void* aClosure)
-    {
-      AssertIsOnMainThread();
-      MOZ_ASSERT(aSharedWorker);
-      MOZ_ASSERT(aClosure);
-
-      auto closure = static_cast<Closure*>(aClosure);
-      MOZ_ASSERT(closure->mWindow);
-
-      if (aSharedWorker->GetOwner() == closure->mWindow) {
-        closure->mSharedWorkers.AppendElement(aSharedWorker);
-      } else {
-        MOZ_ASSERT(!SameCOMIdentity(aSharedWorker->GetOwner(),
-                                    closure->mWindow));
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
-  Closure closure(aWindow);
-
-  mSharedWorkers.EnumerateRead(Closure::Collect, &closure);
-
-  for (uint32_t index = 0; index < closure.mSharedWorkers.Length(); index++) {
-    closure.mSharedWorkers[index]->Close();
+  nsAutoTArray<nsRefPtr<SharedWorker>, 10> sharedWorkers;
+
+  for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
+    if (mSharedWorkers[i]->GetOwner() == aWindow) {
+      sharedWorkers.AppendElement(mSharedWorkers[i]);
+    } else {
+      MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
+                                  aWindow));
+    }
+  }
+
+  for (uint32_t index = 0; index < sharedWorkers.Length(); index++) {
+    sharedWorkers[index]->Close();
   }
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::WorkerScriptLoaded()
 {
   AssertIsOnMainThread();
@@ -4583,19 +4365,16 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
         if (!mControlQueue.IsEmpty()) {
           WorkerControlRunnable* runnable;
           while (mControlQueue.Pop(runnable)) {
             runnable->Cancel();
             runnable->Release();
           }
         }
 
-        // Clear away our MessagePorts.
-        mWorkerPorts.Clear();
-
         // Unroot the globals
         mScope = nullptr;
         mDebuggerScope = nullptr;
 
         return;
       }
     }
 
@@ -5523,18 +5302,16 @@ WorkerPrivate::AssertValidSyncLoop(nsIEv
 }
 #endif
 
 void
 WorkerPrivate::PostMessageToParentInternal(
                             JSContext* aCx,
                             JS::Handle<JS::Value> aMessage,
                             const Optional<Sequence<JS::Value>>& aTransferable,
-                            bool aToMessagePort,
-                            uint64_t aMessagePortSerial,
                             ErrorResult& aRv)
 {
   AssertIsOnWorkerThread();
 
   JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
   if (aTransferable.WasPassed()) {
     const Sequence<JS::Value>& realTransferable = aTransferable.Value();
 
@@ -5549,50 +5326,29 @@ WorkerPrivate::PostMessageToParentIntern
       aRv = NS_ERROR_OUT_OF_MEMORY;
       return;
     }
     transferable.setObject(*array);
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(this,
-                             WorkerRunnable::ParentThreadUnchangedBusyCount,
-                             aToMessagePort, aMessagePortSerial);
+                             WorkerRunnable::ParentThreadUnchangedBusyCount);
 
   runnable->Write(aCx, aMessage, transferable, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   if (!runnable->Dispatch(aCx)) {
     aRv = NS_ERROR_FAILURE;
   }
 }
 
 void
-WorkerPrivate::PostMessageToParentMessagePort(
-                             JSContext* aCx,
-                             uint64_t aMessagePortSerial,
-                             JS::Handle<JS::Value> aMessage,
-                             const Optional<Sequence<JS::Value>>& aTransferable,
-                             ErrorResult& aRv)
-{
-  AssertIsOnWorkerThread();
-
-  if (!mWorkerPorts.GetWeak(aMessagePortSerial)) {
-    // This port has been closed from the main thread. There's no point in
-    // sending this message so just bail.
-    return;
-  }
-
-  PostMessageToParentInternal(aCx, aMessage, aTransferable, true,
-                              aMessagePortSerial, aRv);
-}
-
-void
 WorkerPrivate::EnterDebuggerEventLoop()
 {
   AssertIsOnWorkerThread();
 
   JSContext* cx = GetJSContext();
   MOZ_ASSERT(cx);
 
   uint32_t currentEventLoopLevel = ++mDebuggerEventLoopLevel;
@@ -6439,85 +6195,65 @@ WorkerPrivate::EndCTypesCall()
     mBlockedForMemoryReporter = false;
   }
 
   // Make sure the periodic timer is running before we start running JS again.
   SetGCTimerMode(PeriodicTimer);
 }
 
 bool
-WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial)
+WorkerPrivate::ConnectMessagePort(JSContext* aCx,
+                                  MessagePortIdentifier& aIdentifier)
 {
   AssertIsOnWorkerThread();
 
-  NS_ASSERTION(!mWorkerPorts.GetWeak(aMessagePortSerial),
-               "Already have this port registered!");
-
   WorkerGlobalScope* globalScope = GlobalScope();
 
   JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
   MOZ_ASSERT(jsGlobal);
 
-  nsRefPtr<MessagePort> port = new MessagePort(this, aMessagePortSerial);
+  // This MessagePortIdentifier is used to create a new port, still connected
+  // with the other one, but in the worker thread.
+  ErrorResult rv;
+  nsRefPtr<MessagePort> port = MessagePort::Create(nullptr, aIdentifier, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return false;
+  }
 
   GlobalObject globalObject(aCx, jsGlobal);
   if (globalObject.Failed()) {
     return false;
   }
 
   RootedDictionary<MessageEventInit> init(aCx);
   init.mBubbles = false;
   init.mCancelable = false;
   init.mSource.SetValue().SetAsMessagePort() = port;
 
-  ErrorResult rv;
-
   nsRefPtr<MessageEvent> event =
     MessageEvent::Constructor(globalObject,
                               NS_LITERAL_STRING("connect"), init, rv);
 
   event->SetTrusted(true);
 
-  nsTArray<nsRefPtr<MessagePortBase>> ports;
+  nsTArray<nsRefPtr<MessagePort>> ports;
   ports.AppendElement(port);
 
   nsRefPtr<MessagePortList> portList =
     new MessagePortList(static_cast<nsIDOMEventTarget*>(globalScope), ports);
   event->SetPorts(portList);
 
-  mWorkerPorts.Put(aMessagePortSerial, port);
-
   nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
 
   nsEventStatus dummy = nsEventStatus_eIgnore;
   globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
+
   return true;
 }
 
-void
-WorkerPrivate::DisconnectMessagePort(uint64_t aMessagePortSerial)
-{
-  AssertIsOnWorkerThread();
-
-  mWorkerPorts.Remove(aMessagePortSerial);
-}
-
-workers::MessagePort*
-WorkerPrivate::GetMessagePort(uint64_t aMessagePortSerial)
-{
-  AssertIsOnWorkerThread();
-
-  nsRefPtr<MessagePort> port;
-  if (mWorkerPorts.Get(aMessagePortSerial, getter_AddRefs(port))) {
-    return port;
-  }
-
-  return nullptr;
-}
-
 WorkerGlobalScope*
 WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx)
 {
   AssertIsOnWorkerThread();
 
   if (!mScope) {
     nsRefPtr<WorkerGlobalScope> globalScope;
     if (IsSharedWorker()) {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -45,31 +45,32 @@ class nsIURI;
 
 namespace JS {
 struct RuntimeStats;
 } // namespace JS
 
 namespace mozilla {
 namespace dom {
 class Function;
+class MessagePort;
+class MessagePortIdentifier;
 class StructuredCloneHelper;
 } // namespace dom
 namespace ipc {
 class PrincipalInfo;
 } // namespace ipc
 } // namespace mozilla
 
 struct PRThread;
 
 class ReportDebuggerErrorRunnable;
 
 BEGIN_WORKERS_NAMESPACE
 
 class AutoSyncLoopHolder;
-class MessagePort;
 class SharedWorker;
 class ServiceWorkerClientInfo;
 class WorkerControlRunnable;
 class WorkerDebugger;
 class WorkerDebuggerGlobalScope;
 class WorkerGlobalScope;
 class WorkerPrivate;
 class WorkerRunnable;
@@ -172,20 +173,19 @@ private:
   // Only used for top level workers.
   nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
 
   // Protected by mMutex.
   JSSettings mJSSettings;
 
   // Only touched on the parent thread (currently this is always the main
   // thread as SharedWorkers are always top-level).
-  nsDataHashtable<nsUint64HashKey, SharedWorker*> mSharedWorkers;
+  nsTArray<SharedWorker*> mSharedWorkers;
 
   uint64_t mBusyCount;
-  uint64_t mMessagePortSerial;
   Status mParentStatus;
   bool mParentFrozen;
   bool mIsChromeWorker;
   bool mMainThreadObjectsForgotten;
   WorkerType mWorkerType;
   TimeStamp mCreationTimeStamp;
   DOMHighResTimeStamp mCreationTimeHighRes;
   TimeStamp mNowBaseTimeStamp;
@@ -221,17 +221,16 @@ private:
   TerminatePrivate(JSContext* aCx)
   {
     return NotifyPrivate(aCx, Terminating);
   }
 
   void
   PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const Optional<Sequence<JS::Value>>& aTransferable,
-                      bool aToMessagePort, uint64_t aMessagePortSerial,
                       ServiceWorkerClientInfo* aClientInfo,
                       ErrorResult& aRv);
 
   nsresult
   DispatchPrivate(already_AddRefed<WorkerRunnable>&& aRunnable, nsIEventTarget* aSyncLoopTarget);
 
 public:
   virtual JSObject*
@@ -323,39 +322,26 @@ public:
   void
   ForgetMainThreadObjects(nsTArray<nsCOMPtr<nsISupports> >& aDoomed);
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Optional<Sequence<JS::Value> >& aTransferable,
               ErrorResult& aRv)
   {
-    PostMessageInternal(aCx, aMessage, aTransferable, false, 0, nullptr, aRv);
+    PostMessageInternal(aCx, aMessage, aTransferable, nullptr, aRv);
   }
 
   void
   PostMessageToServiceWorker(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
                              nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
                              ErrorResult& aRv);
 
   void
-  PostMessageToMessagePort(JSContext* aCx,
-                           uint64_t aMessagePortSerial,
-                           JS::Handle<JS::Value> aMessage,
-                           const Optional<Sequence<JS::Value> >& aTransferable,
-                           ErrorResult& aRv);
-
-  bool
-  DispatchMessageEventToMessagePort(
-                               JSContext* aCx,
-                               uint64_t aMessagePortSerial,
-                               StructuredCloneHelper& aHelper);
-
-  void
   UpdateRuntimeOptions(JSContext* aCx,
                        const JS::RuntimeOptions& aRuntimeOptions);
 
   void
   UpdateLanguages(JSContext* aCx, const nsTArray<nsString>& aLanguages);
 
   void
   UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue);
@@ -374,17 +360,18 @@ public:
 
   void
   CycleCollect(JSContext* aCx, bool aDummy);
 
   void
   OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline);
 
   bool
-  RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
+  RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker,
+                       MessagePort* aPort);
 
   void
   UnregisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
 
   void
   BroadcastErrorToSharedWorkers(JSContext* aCx,
                                 const nsAString& aMessage,
                                 const nsAString& aFilename,
@@ -753,23 +740,16 @@ public:
   }
 
   const nsCString&
   SharedWorkerName() const
   {
     return mSharedWorkerName;
   }
 
-  uint64_t
-  NextMessagePortSerial()
-  {
-    AssertIsOnMainThread();
-    return mMessagePortSerial++;
-  }
-
   bool
   IsStorageAllowed() const
   {
     return mLoadInfo.mStorageAllowed;
   }
 
   bool
   IsInPrivateBrowsing() const
@@ -947,18 +927,16 @@ class WorkerPrivate : public WorkerPriva
   nsCOMPtr<nsITimer> mTimer;
 
   nsCOMPtr<nsITimer> mGCTimer;
   nsCOMPtr<nsIEventTarget> mPeriodicGCTimerTarget;
   nsCOMPtr<nsIEventTarget> mIdleGCTimerTarget;
 
   nsRefPtr<MemoryReporter> mMemoryReporter;
 
-  nsRefPtrHashtable<nsUint64HashKey, MessagePort> mWorkerPorts;
-
   // fired on the main thread if the worker script fails to load
   nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
 
   TimeStamp mKillTime;
   uint32_t mErrorHandlerRecursionCount;
   uint32_t mNextTimeoutId;
   Status mStatus;
   bool mFrozen;
@@ -1071,23 +1049,22 @@ public:
   }
 
   void
   PostMessageToParent(JSContext* aCx,
                       JS::Handle<JS::Value> aMessage,
                       const Optional<Sequence<JS::Value>>& aTransferable,
                       ErrorResult& aRv)
   {
-    PostMessageToParentInternal(aCx, aMessage, aTransferable, false, 0, aRv);
+    PostMessageToParentInternal(aCx, aMessage, aTransferable, aRv);
   }
 
   void
   PostMessageToParentMessagePort(
                              JSContext* aCx,
-                             uint64_t aMessagePortSerial,
                              JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
                              ErrorResult& aRv);
 
   void
   EnterDebuggerEventLoop();
 
   void
@@ -1234,23 +1211,17 @@ public:
   EndCTypesCallback()
   {
     // If a callback is ending then we need to do the exact same thing as
     // when a ctypes call begins.
     BeginCTypesCall();
   }
 
   bool
-  ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial);
-
-  void
-  DisconnectMessagePort(uint64_t aMessagePortSerial);
-
-  MessagePort*
-  GetMessagePort(uint64_t aMessagePortSerial);
+  ConnectMessagePort(JSContext* aCx, MessagePortIdentifier& aIdentifier);
 
   WorkerGlobalScope*
   GetOrCreateGlobalScope(JSContext* aCx);
 
   WorkerDebuggerGlobalScope*
   CreateDebuggerGlobalScope(JSContext* aCx);
 
   bool
@@ -1440,18 +1411,16 @@ private:
 
   void
   WaitForWorkerEvents(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT);
 
   void
   PostMessageToParentInternal(JSContext* aCx,
                               JS::Handle<JS::Value> aMessage,
                               const Optional<Sequence<JS::Value>>& aTransferable,
-                              bool aToMessagePort,
-                              uint64_t aMessagePortSerial,
                               ErrorResult& aRv);
 
   void
   GetAllPreferences(bool aPreferences[WORKERPREF_COUNT]) const
   {
     AssertIsOnWorkerThread();
     memcpy(aPreferences, mPreferences, WORKERPREF_COUNT * sizeof(bool));
   }
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -24,17 +24,16 @@ EXPORTS.mozilla.dom.workers += [
 ]
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
     'DataStore.h',
     'DataStoreCursor.h',
     'FileReaderSync.h',
     'Location.h',
-    'MessagePort.h',
     'Navigator.h',
     'Performance.h',
     'ServiceWorker.h',
     'ServiceWorkerClient.h',
     'ServiceWorkerClients.h',
     'ServiceWorkerWindowClient.h',
     'SharedWorker.h',
     'URL.h',
@@ -51,17 +50,16 @@ XPIDL_SOURCES += [
 ]
 
 UNIFIED_SOURCES += [
     'ChromeWorkerScope.cpp',
     'DataStore.cpp',
     'DataStoreCursor.cpp',
     'FileReaderSync.cpp',
     'Location.cpp',
-    'MessagePort.cpp',
     'Navigator.cpp',
     'Performance.cpp',
     'Principal.cpp',
     'RegisterBindings.cpp',
     'RuntimeService.cpp',
     'ScriptLoader.cpp',
     'ServiceWorker.cpp',
     'ServiceWorkerClient.cpp',
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -105,16 +105,17 @@ support-files =
   bug1132395_sharedWorker.js
   bug1132924_worker.js
   empty.html
   worker_performance_user_timing.js
   worker_performance_observer.js
   sharedworker_performance_user_timing.js
   referrer.sjs
   performance_observer.html
+  sharedWorker_ports.js
 
 [test_404.html]
 [test_atob.html]
 [test_blobConstructor.html]
 [test_blobWorkers.html]
 [test_bug949946.html]
 [test_bug978260.html]
 [test_bug998474.html]
@@ -216,8 +217,9 @@ skip-if = buildapp == 'b2g' || toolkit =
 skip-if = buildapp == 'b2g' || e10s
 [test_xhr_responseURL.html]
 [test_xhr_system.html]
 skip-if = buildapp == 'b2g' || e10s
 [test_xhr_timeout.html]
 skip-if = (os == "win") || (os == "mac") || toolkit == 'android' || e10s #bug 798220
 [test_xhrAbort.html]
 [test_referrer.html]
+[test_sharedWorker_ports.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/sharedWorker_ports.js
@@ -0,0 +1,23 @@
+var port;
+onconnect = function(evt) {
+  evt.source.postMessage({ type: "connected" });
+
+  if (!port) {
+    port = evt.source;
+    evt.source.onmessage = function(evtFromPort) {
+      port.postMessage({type: "status",
+                        test: "Port from the main-thread!" == evtFromPort.data,
+                        msg: "The message is coming from the main-thread"});
+      port.postMessage({type: "status",
+                        test: (evtFromPort.ports.length == 1),
+                        msg: "1 port transferred"});
+
+      evtFromPort.ports[0].onmessage = function(evtFromPort2) {
+        port.postMessage({type: "status",
+                          test: (evtFromPort2.data.type == "connected"),
+                          msg: "The original message received" });
+        port.postMessage({type: "finish"});
+      }
+    }
+  }
+}
--- a/dom/workers/test/test_sharedWorker.html
+++ b/dom/workers/test/test_sharedWorker.html
@@ -43,31 +43,34 @@
           var receivedError;
 
           worker.port.onmessage = function(event) {
             ok(event instanceof MessageEvent, "Got a MessageEvent");
             ok(event.target === worker.port,
                "MessageEvent has correct 'target' property");
             is(event.data, sentMessage, "Got correct message");
             ok(receivedMessage === undefined, "Haven't gotten message yet");
-            ok(receivedError === undefined, "Haven't gotten error yet");
             receivedMessage = event.data;
+            if (receivedError) {
+              SimpleTest.finish();
+            }
           };
 
           worker.onerror = function(event) {
             ok(event instanceof ErrorEvent, "Got an ErrorEvent");
             is(event.message, "Error: " + sentMessage, "Got correct error");
             is(event.filename, errorFilename, "Got correct filename");
             is(event.lineno, errorLine, "Got correct lineno");
             is(event.colno, errorColumn, "Got correct column");
-            ok(receivedMessage !== undefined, "Got message already");
             ok(receivedError === undefined, "Haven't gotten error yet");
             receivedError = event.message;
             event.preventDefault();
-            SimpleTest.finish();
+            if (receivedMessage) {
+              SimpleTest.finish();
+            }
           };
 
           worker.port.postMessage(sentMessage);
         });
 
         SimpleTest.waitForExplicitFinish();
 
       </script>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_sharedWorker_ports.html
@@ -0,0 +1,42 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Test for MessagePort and SharedWorkers</title>
+    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  </head>
+  <body>
+    <script class="testbody" type="text/javascript">
+
+SpecialPowers.pushPrefEnv({ set: [["dom.workers.sharedWorkers.enabled", true]]},
+function() {
+  var sw1 = new SharedWorker('sharedWorker_ports.js');
+  sw1.port.onmessage = function(event) {
+    if (event.data.type == "connected") {
+      ok(true, "The SharedWorker is alive.");
+
+      var sw2 = new SharedWorker('sharedWorker_ports.js');
+      sw1.port.postMessage("Port from the main-thread!", [sw2.port]);
+      return;
+    }
+
+    if (event.data.type == "status") {
+      ok(event.data.test, event.data.msg);
+      return;
+    }
+
+    if (event.data.type == "finish") {
+      SimpleTest.finish();
+    }
+  }
+});
+
+SimpleTest.waitForExplicitFinish();
+    </script>
+  </body>
+</html>
+