Bug 1111971 - A better life-time management of aListener and aContext in WebSocketChannel. r=smaug, a=abillings
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 13 Jan 2015 14:03:56 -0500
changeset 242872 19e248751a1c
parent 242871 0a648dfd0459
child 242874 1d99e9a39847
push id4323
push userryanvm@gmail.com
push date2015-01-14 15:46 +0000
treeherdermozilla-beta@19e248751a1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, abillings
bugs1111971
milestone36.0
Bug 1111971 - A better life-time management of aListener and aContext in WebSocketChannel. r=smaug, a=abillings
dom/base/WebSocket.cpp
netwerk/protocol/websocket/BaseWebSocketChannel.cpp
netwerk/protocol/websocket/BaseWebSocketChannel.h
netwerk/protocol/websocket/WebSocketChannel.cpp
netwerk/protocol/websocket/WebSocketChannelChild.cpp
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -641,23 +641,24 @@ WebSocketImpl::DoOnMessageAvailable(cons
   }
 
   if (readyState == WebSocket::OPEN) {
     // Dispatch New Message
     nsresult rv = mWebSocket->CreateAndDispatchMessageEvent(aMsg, isBinary);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch the message event");
     }
-  } else {
-    // CLOSING should be the only other state where it's possible to get msgs
-    // from channel: Spec says to drop them.
-    MOZ_ASSERT(readyState == WebSocket::CLOSING,
-               "Received message while CONNECTING or CLOSED");
+
+    return NS_OK;
   }
 
+  // CLOSING should be the only other state where it's possible to get msgs
+  // from channel: Spec says to drop them.
+  MOZ_ASSERT(readyState == WebSocket::CLOSING,
+             "Received message while CONNECTING or CLOSED");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::OnMessageAvailable(nsISupports* aContext,
                                   const nsACString& aMsg)
 {
   AssertIsOnTargetThread();
@@ -713,24 +714,27 @@ WebSocketImpl::OnStart(nsISupports* aCon
     mChannel->GetProtocol(mWebSocket->mEstablishedProtocol);
   }
 
   mChannel->GetExtensions(mWebSocket->mEstablishedExtensions);
   UpdateURI();
 
   mWebSocket->SetReadyState(WebSocket::OPEN);
 
+  // Let's keep the object alive because the webSocket can be CCed in the
+  // onopen callback.
+  nsRefPtr<WebSocket> webSocket = mWebSocket;
+
   // Call 'onopen'
-  rv = mWebSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
+  rv = webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch the open event");
   }
 
-  mWebSocket->UpdateMustKeepAlive();
-
+  webSocket->UpdateMustKeepAlive();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::OnStop(nsISupports* aContext, nsresult aStatusCode)
 {
   AssertIsOnTargetThread();
 
@@ -1591,33 +1595,37 @@ WebSocketImpl::DispatchConnectionCloseEv
   AssertIsOnTargetThread();
 
   if (mDisconnectingOrDisconnected) {
     return;
   }
 
   mWebSocket->SetReadyState(WebSocket::CLOSED);
 
+  // Let's keep the object alive because the webSocket can be CCed in the
+  // onerror or in the onclose callback.
+  nsRefPtr<WebSocket> webSocket = mWebSocket;
+
   // Call 'onerror' if needed
   if (mFailed) {
     nsresult rv =
-      mWebSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
+      webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch the error event");
     }
   }
 
-  nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mCloseEventWasClean,
-                                                        mCloseEventCode,
-                                                        mCloseEventReason);
+  nsresult rv = webSocket->CreateAndDispatchCloseEvent(mCloseEventWasClean,
+                                                       mCloseEventCode,
+                                                       mCloseEventReason);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch the close event");
   }
 
-  mWebSocket->UpdateMustKeepAlive();
+  webSocket->UpdateMustKeepAlive();
   Disconnect();
 }
 
 nsresult
 WebSocket::CreateAndDispatchSimpleEvent(const nsAString& aName)
 {
   MOZ_ASSERT(mImpl);
   AssertIsOnTargetThread();
--- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebSocketLog.h"
 #include "BaseWebSocketChannel.h"
 #include "MainThreadUtils.h"
 #include "nsILoadGroup.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsAutoPtr.h"
+#include "nsProxyRelease.h"
 #include "nsStandardURL.h"
 
 #if defined(PR_LOGGING)
 PRLogModuleInfo *webSocketLog = nullptr;
 #endif
 
 namespace mozilla {
 namespace net {
@@ -279,10 +280,31 @@ BaseWebSocketChannel::RetargetDeliveryTo
   MOZ_ASSERT(!mTargetThread, "Delivery target should be set once, before AsyncOpen");
   MOZ_ASSERT(!mWasOpened, "Should not be called after AsyncOpen!");
 
   mTargetThread = do_QueryInterface(aTargetThread);
   MOZ_ASSERT(mTargetThread);
   return NS_OK;
 }
 
+BaseWebSocketChannel::ListenerAndContextContainer::ListenerAndContextContainer(
+                                               nsIWebSocketListener* aListener,
+                                               nsISupports* aContext)
+  : mListener(aListener)
+  , mContext(aContext)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mListener);
+}
+
+BaseWebSocketChannel::ListenerAndContextContainer::~ListenerAndContextContainer()
+{
+  MOZ_ASSERT(mListener);
+
+  nsCOMPtr<nsIThread> mainThread;
+  NS_GetMainThread(getter_AddRefs(mainThread));
+
+  NS_ProxyRelease(mainThread, mListener, false);
+  NS_ProxyRelease(mainThread, mContext, false);
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/websocket/BaseWebSocketChannel.h
+++ b/netwerk/protocol/websocket/BaseWebSocketChannel.h
@@ -52,21 +52,35 @@ class BaseWebSocketChannel : public nsIW
   NS_IMETHOD SetPingInterval(uint32_t aSeconds);
   NS_IMETHOD GetPingTimeout(uint32_t *aSeconds);
   NS_IMETHOD SetPingTimeout(uint32_t aSeconds);
 
   // Off main thread URI access.
   virtual void GetEffectiveURL(nsAString& aEffectiveURL) const = 0;
   virtual bool IsEncrypted() const = 0;
 
+  class ListenerAndContextContainer MOZ_FINAL
+  {
+  public:
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ListenerAndContextContainer)
+
+    ListenerAndContextContainer(nsIWebSocketListener* aListener,
+                                nsISupports* aContext);
+
+    nsCOMPtr<nsIWebSocketListener> mListener;
+    nsCOMPtr<nsISupports>          mContext;
+
+  private:
+    ~ListenerAndContextContainer();
+  };
+
  protected:
   nsCOMPtr<nsIURI>                mOriginalURI;
   nsCOMPtr<nsIURI>                mURI;
-  nsCOMPtr<nsIWebSocketListener>  mListener;
-  nsCOMPtr<nsISupports>           mContext;
+  nsRefPtr<ListenerAndContextContainer> mListenerMT;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsCOMPtr<nsILoadGroup>          mLoadGroup;
   nsCOMPtr<nsILoadInfo>           mLoadInfo;
   nsCOMPtr<nsIEventTarget>        mTargetThread;
 
   nsCString                       mProtocol;
   nsCString                       mOrigin;
 
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -549,138 +549,158 @@ StaticMutex           nsWSAdmissionManag
 // CallOnMessageAvailable
 //-----------------------------------------------------------------------------
 
 class CallOnMessageAvailable MOZ_FINAL : public nsIRunnable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  CallOnMessageAvailable(WebSocketChannel *aChannel,
-                         nsCString        &aData,
-                         int32_t           aLen)
+  CallOnMessageAvailable(WebSocketChannel* aChannel,
+                         nsACString& aData,
+                         int32_t aLen)
     : mChannel(aChannel),
+      mListenerMT(aChannel->mListenerMT),
       mData(aData),
       mLen(aLen) {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(mChannel->IsOnTargetThread());
 
-    if (mLen < 0)
-      mChannel->mListener->OnMessageAvailable(mChannel->mContext, mData);
-    else
-      mChannel->mListener->OnBinaryMessageAvailable(mChannel->mContext, mData);
+    if (mListenerMT) {
+      if (mLen < 0) {
+        mListenerMT->mListener->OnMessageAvailable(mListenerMT->mContext,
+                                                   mData);
+      } else {
+        mListenerMT->mListener->OnBinaryMessageAvailable(mListenerMT->mContext,
+                                                         mData);
+      }
+    }
+
     return NS_OK;
   }
 
 private:
   ~CallOnMessageAvailable() {}
 
-  nsRefPtr<WebSocketChannel>        mChannel;
-  nsCString                         mData;
-  int32_t                           mLen;
+  nsRefPtr<WebSocketChannel> mChannel;
+  nsRefPtr<BaseWebSocketChannel::ListenerAndContextContainer> mListenerMT;
+  nsCString mData;
+  int32_t mLen;
 };
 NS_IMPL_ISUPPORTS(CallOnMessageAvailable, nsIRunnable)
 
 //-----------------------------------------------------------------------------
 // CallOnStop
 //-----------------------------------------------------------------------------
 
 class CallOnStop MOZ_FINAL : public nsIRunnable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  CallOnStop(WebSocketChannel *aChannel,
-             nsresult          aReason)
+  CallOnStop(WebSocketChannel* aChannel,
+             nsresult aReason)
     : mChannel(aChannel),
-      mReason(aReason) {}
+      mListenerMT(mChannel->mListenerMT),
+      mReason(aReason)
+  {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(mChannel->IsOnTargetThread());
 
     nsWSAdmissionManager::OnStopSession(mChannel, mReason);
 
-    if (mChannel->mListener) {
-      mChannel->mListener->OnStop(mChannel->mContext, mReason);
-      mChannel->mListener = nullptr;
-      mChannel->mContext = nullptr;
+    if (mListenerMT) {
+      mListenerMT->mListener->OnStop(mListenerMT->mContext, mReason);
+      mChannel->mListenerMT = nullptr;
     }
+
     return NS_OK;
   }
 
 private:
   ~CallOnStop() {}
 
-  nsRefPtr<WebSocketChannel>        mChannel;
-  nsresult                          mReason;
+  nsRefPtr<WebSocketChannel> mChannel;
+  nsRefPtr<BaseWebSocketChannel::ListenerAndContextContainer> mListenerMT;
+  nsresult mReason;
 };
 NS_IMPL_ISUPPORTS(CallOnStop, nsIRunnable)
 
 //-----------------------------------------------------------------------------
 // CallOnServerClose
 //-----------------------------------------------------------------------------
 
 class CallOnServerClose MOZ_FINAL : public nsIRunnable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  CallOnServerClose(WebSocketChannel *aChannel,
-                    uint16_t          aCode,
-                    nsCString        &aReason)
+  CallOnServerClose(WebSocketChannel* aChannel,
+                    uint16_t aCode,
+                    nsACString& aReason)
     : mChannel(aChannel),
+      mListenerMT(mChannel->mListenerMT),
       mCode(aCode),
       mReason(aReason) {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(mChannel->IsOnTargetThread());
 
-    mChannel->mListener->OnServerClose(mChannel->mContext, mCode, mReason);
+    if (mListenerMT) {
+      mListenerMT->mListener->OnServerClose(mListenerMT->mContext, mCode,
+                                            mReason);
+    }
     return NS_OK;
   }
 
 private:
   ~CallOnServerClose() {}
 
-  nsRefPtr<WebSocketChannel>        mChannel;
-  uint16_t                          mCode;
-  nsCString                         mReason;
+  nsRefPtr<WebSocketChannel> mChannel;
+  nsRefPtr<BaseWebSocketChannel::ListenerAndContextContainer> mListenerMT;
+  uint16_t mCode;
+  nsCString mReason;
 };
 NS_IMPL_ISUPPORTS(CallOnServerClose, nsIRunnable)
 
 //-----------------------------------------------------------------------------
 // CallAcknowledge
 //-----------------------------------------------------------------------------
 
 class CallAcknowledge MOZ_FINAL : public nsCancelableRunnable
 {
 public:
-  CallAcknowledge(WebSocketChannel *aChannel,
-                  uint32_t          aSize)
+  CallAcknowledge(WebSocketChannel* aChannel,
+                  uint32_t aSize)
     : mChannel(aChannel),
+      mListenerMT(mChannel->mListenerMT),
       mSize(aSize) {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(mChannel->IsOnTargetThread());
 
     LOG(("WebSocketChannel::CallAcknowledge: Size %u\n", mSize));
-    mChannel->mListener->OnAcknowledge(mChannel->mContext, mSize);
+    if (mListenerMT) {
+      mListenerMT->mListener->OnAcknowledge(mListenerMT->mContext, mSize);
+    }
     return NS_OK;
   }
 
 private:
   ~CallAcknowledge() {}
 
-  nsRefPtr<WebSocketChannel>        mChannel;
-  uint32_t                          mSize;
+  nsRefPtr<WebSocketChannel> mChannel;
+  nsRefPtr<BaseWebSocketChannel::ListenerAndContextContainer> mListenerMT;
+  uint32_t mSize;
 };
 
 //-----------------------------------------------------------------------------
 // CallOnTransportAvailable
 //-----------------------------------------------------------------------------
 
 class CallOnTransportAvailable MOZ_FINAL : public nsIRunnable
 {
@@ -1070,27 +1090,17 @@ WebSocketChannel::~WebSocketChannel()
     NS_ProxyRelease(mainThread, forgettable, false);
   }
 
   if (mOriginalURI) {
     mOriginalURI.forget(&forgettable);
     NS_ProxyRelease(mainThread, forgettable, false);
   }
 
-  if (mListener) {
-    nsIWebSocketListener *forgettableListener;
-    mListener.forget(&forgettableListener);
-    NS_ProxyRelease(mainThread, forgettableListener, false);
-  }
-
-  if (mContext) {
-    nsISupports *forgettableContext;
-    mContext.forget(&forgettableContext);
-    NS_ProxyRelease(mainThread, forgettableContext, false);
-  }
+  mListenerMT = nullptr;
 
   if (mLoadGroup) {
     nsILoadGroup *forgettableGroup;
     mLoadGroup.forget(&forgettableGroup);
     NS_ProxyRelease(mainThread, forgettableGroup, false);
   }
 
   if (mLoadInfo) {
@@ -1468,17 +1478,17 @@ WebSocketChannel::ProcessInput(uint8_t *
       LOG(("WebSocketChannel:: ignoring read frame code %d after close\n",
                  opcode));
       // nop
     } else if (mStopped) {
       LOG(("WebSocketChannel:: ignoring read frame code %d after completion\n",
            opcode));
     } else if (opcode == kText) {
       LOG(("WebSocketChannel:: text frame received\n"));
-      if (mListener) {
+      if (mListenerMT) {
         nsCString utf8Data;
         if (!utf8Data.Assign((const char *)payload, payloadLength,
                              mozilla::fallible_t()))
           return NS_ERROR_OUT_OF_MEMORY;
 
         // Section 8.1 says to fail connection if invalid utf-8 in text message
         if (!IsUTF8(utf8Data, false)) {
           LOG(("WebSocketChannel:: text frame invalid utf-8\n"));
@@ -1527,17 +1537,17 @@ WebSocketChannel::ProcessInput(uint8_t *
                  mServerCloseReason.get()));
           }
         }
 
         if (mCloseTimer) {
           mCloseTimer->Cancel();
           mCloseTimer = nullptr;
         }
-        if (mListener) {
+        if (mListenerMT) {
           mTargetThread->Dispatch(new CallOnServerClose(this, mServerCloseCode,
                                                         mServerCloseReason),
                                   NS_DISPATCH_NORMAL);
         }
 
         if (mClientClosed)
           ReleaseSession();
       } else if (opcode == kPing) {
@@ -1563,17 +1573,17 @@ WebSocketChannel::ProcessInput(uint8_t *
         payload = mFramePtr;
         avail -= payloadLength;
         if (mBuffered)
           mBuffered -= framingLength + payloadLength;
         payloadLength = 0;
       }
     } else if (opcode == kBinary) {
       LOG(("WebSocketChannel:: binary frame received\n"));
-      if (mListener) {
+      if (mListenerMT) {
         nsCString binaryData((const char *)payload, payloadLength);
         mTargetThread->Dispatch(new CallOnMessageAvailable(this, binaryData,
                                                            payloadLength),
                                 NS_DISPATCH_NORMAL);
         // To add the header to 'Networking Dashboard' log
         if (mConnectionLogService && !mPrivateBrowsing) {
           mConnectionLogService->NewMsgReceived(mHost, mSerial, count);
           LOG(("Added new received msg for %s", mHost.get()));
@@ -2123,18 +2133,21 @@ WebSocketChannel::StopSession(nsresult r
   mInflateReader = nullptr;
   mInflateStream = nullptr;
 
   delete mCompressor;
   mCompressor = nullptr;
 
   if (!mCalledOnStop) {
     mCalledOnStop = 1;
-    mTargetThread->Dispatch(new CallOnStop(this, reason),
-                            NS_DISPATCH_NORMAL);
+
+    nsWSAdmissionManager::OnStopSession(this, reason);
+
+    nsRefPtr<CallOnStop> runnable = new CallOnStop(this, reason);
+    mTargetThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
   }
 }
 
 void
 WebSocketChannel::AbortSession(nsresult reason)
 {
   LOG(("WebSocketChannel::AbortSession() %p [reason %x] stopped = %d\n",
        this, reason, mStopped));
@@ -2417,20 +2430,20 @@ WebSocketChannel::StartWebsocketData()
   mDataStarted = 1;
 
   // We're now done CONNECTING, which means we can now open another,
   // perhaps parallel, connection to the same host if one
   // is pending
   nsWSAdmissionManager::OnConnected(this);
 
   LOG(("WebSocketChannel::StartWebsocketData Notifying Listener %p\n",
-       mListener.get()));
-
-  if (mListener) {
-    mListener->OnStart(mContext);
+       mListenerMT ? mListenerMT->mListener.get() : nullptr));
+
+  if (mListenerMT) {
+    mListenerMT->mListener->OnStart(mListenerMT->mContext);
   }
 
   // Start keepalive ping timer, if we're using keepalive.
   if (mPingInterval) {
     nsresult rv;
     mPingTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     if (NS_FAILED(rv)) {
       NS_WARNING("unable to create ping timer. Carrying on.");
@@ -2783,17 +2796,17 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI
 
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
 
   if (!aURI || !aListener) {
     LOG(("WebSocketChannel::AsyncOpen() Uri or Listener null"));
     return NS_ERROR_UNEXPECTED;
   }
 
-  if (mListener || mWasOpened)
+  if (mListenerMT || mWasOpened)
     return NS_ERROR_ALREADY_OPENED;
 
   nsresult rv;
 
   // Ensure target thread is set.
   if (!mTargetThread) {
     mTargetThread = do_GetMainThread();
   }
@@ -2949,18 +2962,17 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI
   rv = observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Only set these if the open was successful:
   //
   mWasOpened = 1;
-  mListener = aListener;
-  mContext = aContext;
+  mListenerMT = new ListenerAndContextContainer(aListener, aContext);
   IncrementSessionCount();
 
   return rv;
 }
 
 NS_IMETHODIMP
 WebSocketChannel::Close(uint16_t code, const nsACString & reason)
 {
--- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp
@@ -199,19 +199,19 @@ WebSocketChannelChild::OnStart(const nsC
                                const bool& aEncrypted)
 {
   LOG(("WebSocketChannelChild::RecvOnStart() %p\n", this));
   SetProtocol(aProtocol);
   mNegotiatedExtensions = aExtensions;
   mEffectiveURL = aEffectiveURL;
   mEncrypted = aEncrypted;
 
-  if (mListener) {
+  if (mListenerMT) {
     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
-    mListener->OnStart(mContext);
+    mListenerMT->mListener->OnStart(mListenerMT->mContext);
   }
 }
 
 class StopEvent : public ChannelEvent
 {
  public:
   StopEvent(WebSocketChannelChild* aChild,
             const nsresult& aStatusCode)
@@ -241,19 +241,19 @@ WebSocketChannelChild::RecvOnStop(const 
   }
   return true;
 }
 
 void
 WebSocketChannelChild::OnStop(const nsresult& aStatusCode)
 {
   LOG(("WebSocketChannelChild::RecvOnStop() %p\n", this));
-  if (mListener) {
+  if (mListenerMT) {
     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
-    mListener->OnStop(mContext, aStatusCode);
+    mListenerMT->mListener->OnStop(mListenerMT->mContext, aStatusCode);
   }
 }
 
 class MessageEvent : public ChannelEvent
 {
  public:
   MessageEvent(WebSocketChannelChild* aChild,
                const nsCString& aMessage,
@@ -290,19 +290,19 @@ WebSocketChannelChild::RecvOnMessageAvai
   }
   return true;
 }
 
 void
 WebSocketChannelChild::OnMessageAvailable(const nsCString& aMsg)
 {
   LOG(("WebSocketChannelChild::RecvOnMessageAvailable() %p\n", this));
-  if (mListener) {
+  if (mListenerMT) {
     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
-    mListener->OnMessageAvailable(mContext, aMsg);
+    mListenerMT->mListener->OnMessageAvailable(mListenerMT->mContext, aMsg);
   }
 }
 
 bool
 WebSocketChannelChild::RecvOnBinaryMessageAvailable(const nsCString& aMsg)
 {
   if (mEventQ->ShouldEnqueue()) {
     mEventQ->Enqueue(new EventTargetDispatcher(
@@ -314,19 +314,20 @@ WebSocketChannelChild::RecvOnBinaryMessa
   }
   return true;
 }
 
 void
 WebSocketChannelChild::OnBinaryMessageAvailable(const nsCString& aMsg)
 {
   LOG(("WebSocketChannelChild::RecvOnBinaryMessageAvailable() %p\n", this));
-  if (mListener) {
+  if (mListenerMT) {
     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
-    mListener->OnBinaryMessageAvailable(mContext, aMsg);
+    mListenerMT->mListener->OnBinaryMessageAvailable(mListenerMT->mContext,
+                                                     aMsg);
   }
 }
 
 class AcknowledgeEvent : public ChannelEvent
 {
  public:
   AcknowledgeEvent(WebSocketChannelChild* aChild,
                    const uint32_t& aSize)
@@ -356,19 +357,19 @@ WebSocketChannelChild::RecvOnAcknowledge
   }
   return true;
 }
 
 void
 WebSocketChannelChild::OnAcknowledge(const uint32_t& aSize)
 {
   LOG(("WebSocketChannelChild::RecvOnAcknowledge() %p\n", this));
-  if (mListener) {
+  if (mListenerMT) {
     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
-    mListener->OnAcknowledge(mContext, aSize);
+    mListenerMT->mListener->OnAcknowledge(mListenerMT->mContext, aSize);
   }
 }
 
 class ServerCloseEvent : public ChannelEvent
 {
  public:
   ServerCloseEvent(WebSocketChannelChild* aChild,
                    const uint16_t aCode,
@@ -404,32 +405,33 @@ WebSocketChannelChild::RecvOnServerClose
   return true;
 }
 
 void
 WebSocketChannelChild::OnServerClose(const uint16_t& aCode,
                                      const nsCString& aReason)
 {
   LOG(("WebSocketChannelChild::RecvOnServerClose() %p\n", this));
-  if (mListener) {
+  if (mListenerMT) {
     AutoEventEnqueuer ensureSerialDispatch(mEventQ);
-    mListener->OnServerClose(mContext, aCode, aReason);
+    mListenerMT->mListener->OnServerClose(mListenerMT->mContext, aCode,
+                                          aReason);
   }
 }
 
 NS_IMETHODIMP
 WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
                                  const nsACString &aOrigin,
                                  nsIWebSocketListener *aListener,
                                  nsISupports *aContext)
 {
   LOG(("WebSocketChannelChild::AsyncOpen() %p\n", this));
 
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
-  NS_ABORT_IF_FALSE(aURI && aListener && !mListener, 
+  NS_ABORT_IF_FALSE(aURI && aListener && !mListenerMT,
                     "Invalid state for WebSocketChannelChild::AsyncOpen");
 
   mozilla::dom::TabChild* tabChild = nullptr;
   nsCOMPtr<nsITabChild> iTabChild;
   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                 NS_GET_IID(nsITabChild),
                                 getter_AddRefs(iTabChild));
   if (iTabChild) {
@@ -449,18 +451,17 @@ WebSocketChannelChild::AsyncOpen(nsIURI 
                                          IPC::SerializedLoadContext(this));
   if (!SendAsyncOpen(uri, nsCString(aOrigin), mProtocol, mEncrypted,
                      mPingInterval, mClientSetPingInterval,
                      mPingResponseTimeout, mClientSetPingTimeout))
     return NS_ERROR_UNEXPECTED;
 
   mOriginalURI = aURI;
   mURI = mOriginalURI;
-  mListener = aListener;
-  mContext = aContext;
+  mListenerMT = new ListenerAndContextContainer(aListener, aContext);
   mOrigin = aOrigin;
   mWasOpened = 1;
 
   return NS_OK;
 }
 
 class CloseEvent : public nsRunnable
 {