Bug 1203802 - Websocket Frame Listener API for devtool Network Inspector - part 4 - IPC, r=michal
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 26 Oct 2015 15:31:00 +0000
changeset 304690 e415ff528bd18f8e5db55051ddee6ec3be2c6d6c
parent 304689 709a898fdc8bbf27f5f250e10e638e207005ae58
child 304691 64dbcad7bc6cd1e3e20583c31cd5b84072809e8a
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal
bugs1203802
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1203802 - Websocket Frame Listener API for devtool Network Inspector - part 4 - IPC, r=michal
ipc/glue/IPCMessageUtils.h
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/protocol/websocket/PWebSocketFrameListener.ipdl
netwerk/protocol/websocket/WebSocketFrame.cpp
netwerk/protocol/websocket/WebSocketFrame.h
netwerk/protocol/websocket/WebSocketFrameListenerChild.cpp
netwerk/protocol/websocket/WebSocketFrameListenerChild.h
netwerk/protocol/websocket/WebSocketFrameListenerParent.cpp
netwerk/protocol/websocket/WebSocketFrameListenerParent.h
netwerk/protocol/websocket/WebSocketFrameService.cpp
netwerk/protocol/websocket/WebSocketFrameService.h
netwerk/protocol/websocket/moz.build
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -9,16 +9,17 @@
 
 #include "base/process_util.h"
 #include "chrome/common/ipc_message_utils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/net/WebSocketFrame.h"
 #include "mozilla/TimeStamp.h"
 #ifdef XP_WIN
 #include "mozilla/TimeStamp_windows.h"
 #endif
 #include "mozilla/TypeTraits.h"
 #include "mozilla/IntegerTypeTraits.h"
 
 #include <stdint.h>
@@ -714,16 +715,32 @@ struct ParamTraits<mozilla::dom::ipc::St
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     LogParam(aParam.DataLength(), aLog);
   }
 };
 
 template <>
+struct ParamTraits<mozilla::net::WebSocketFrameData>
+{
+  typedef mozilla::net::WebSocketFrameData paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    aParam.WriteIPCParams(aMsg);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return aResult->ReadIPCParams(aMsg, aIter);
+  }
+};
+
+template <>
 struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
 {
   typedef mozilla::SerializedStructuredCloneBuffer paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.dataLength);
     if (aParam.dataLength) {
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/WyciwygChannelChild.h"
 #include "mozilla/net/FTPChannelChild.h"
 #include "mozilla/net/WebSocketChannelChild.h"
+#include "mozilla/net/WebSocketFrameListenerChild.h"
 #include "mozilla/net/DNSRequestChild.h"
 #include "mozilla/net/RemoteOpenFileChild.h"
 #include "mozilla/net/ChannelDiverterChild.h"
 #include "mozilla/dom/network/TCPSocketChild.h"
 #include "mozilla/dom/network/TCPServerSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspControllerChild.h"
@@ -153,16 +154,33 @@ NeckoChild::AllocPWebSocketChild(const P
 bool
 NeckoChild::DeallocPWebSocketChild(PWebSocketChild* child)
 {
   WebSocketChannelChild* p = static_cast<WebSocketChannelChild*>(child);
   p->ReleaseIPDLReference();
   return true;
 }
 
+PWebSocketFrameListenerChild*
+NeckoChild::AllocPWebSocketFrameListenerChild(const uint64_t& aInnerWindowID)
+{
+  RefPtr<WebSocketFrameListenerChild> c =
+    new WebSocketFrameListenerChild(aInnerWindowID);
+  return c.forget().take();
+}
+
+bool
+NeckoChild::DeallocPWebSocketFrameListenerChild(PWebSocketFrameListenerChild* aActor)
+{
+  RefPtr<WebSocketFrameListenerChild> c =
+    dont_AddRef(static_cast<WebSocketFrameListenerChild*>(aActor));
+  MOZ_ASSERT(c);
+  return true;
+}
+
 PDataChannelChild*
 NeckoChild::AllocPDataChannelChild(const uint32_t& channelId)
 {
   MOZ_ASSERT_UNREACHABLE("Should never get here");
   return nullptr;
 }
 
 bool
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -74,16 +74,19 @@ protected:
   AllocPChannelDiverterChild(const ChannelDiverterArgs& channel) override;
   virtual bool
   DeallocPChannelDiverterChild(PChannelDiverterChild* actor) override;
   virtual bool RecvAsyncAuthPromptForNestedFrame(const TabId& aNestedFrameId,
                                                  const nsCString& aUri,
                                                  const nsString& aRealm,
                                                  const uint64_t& aCallbackId) override;
   virtual bool RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline) override;
+  virtual PWebSocketFrameListenerChild*
+    AllocPWebSocketFrameListenerChild(const uint64_t& aInnerWindowID) override;
+  virtual bool DeallocPWebSocketFrameListenerChild(PWebSocketFrameListenerChild*) override;
 
   /* Predictor Messsages */
   virtual bool RecvPredOnPredictPreconnect(const URIParams& aURI) override;
   virtual bool RecvPredOnPredictDNS(const URIParams& aURI) override;
 };
 
 /**
  * Reference to the PNecko Child protocol.
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -8,16 +8,17 @@
 #include "necko-config.h"
 #include "nsHttp.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/net/WyciwygChannelParent.h"
 #include "mozilla/net/FTPChannelParent.h"
 #include "mozilla/net/WebSocketChannelParent.h"
+#include "mozilla/net/WebSocketFrameListenerParent.h"
 #include "mozilla/net/DataChannelParent.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspControllerParent.h"
 #include "mozilla/net/RtspChannelParent.h"
 #endif
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/RemoteOpenFileParent.h"
 #include "mozilla/net/ChannelDiverterParent.h"
@@ -342,16 +343,33 @@ NeckoParent::AllocPWebSocketParent(const
 bool
 NeckoParent::DeallocPWebSocketParent(PWebSocketParent* actor)
 {
   WebSocketChannelParent* p = static_cast<WebSocketChannelParent*>(actor);
   p->Release();
   return true;
 }
 
+PWebSocketFrameListenerParent*
+NeckoParent::AllocPWebSocketFrameListenerParent(const uint64_t& aInnerWindowID)
+{
+  RefPtr<WebSocketFrameListenerParent> c =
+    new WebSocketFrameListenerParent(aInnerWindowID);
+  return c.forget().take();
+}
+
+bool
+NeckoParent::DeallocPWebSocketFrameListenerParent(PWebSocketFrameListenerParent* aActor)
+{
+  RefPtr<WebSocketFrameListenerParent> c =
+    dont_AddRef(static_cast<WebSocketFrameListenerParent*>(aActor));
+  MOZ_ASSERT(c);
+  return true;
+}
+
 PDataChannelParent*
 NeckoParent::AllocPDataChannelParent(const uint32_t &channelId)
 {
   RefPtr<DataChannelParent> p = new DataChannelParent();
   return p.forget().take();
 }
 
 bool
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -167,16 +167,19 @@ protected:
                                           const nsCString& aNetworkInterface) override;
   virtual bool DeallocPDNSRequestParent(PDNSRequestParent*) override;
   virtual bool RecvSpeculativeConnect(const URIParams& aURI, const bool& aAnonymous) override;
   virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
                                    const uint16_t& flags) override;
   virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
                                          const uint16_t& flags,
                                          const nsresult& reason) override;
+  virtual PWebSocketFrameListenerParent*
+    AllocPWebSocketFrameListenerParent(const uint64_t& aInnerWindowID) override;
+  virtual bool DeallocPWebSocketFrameListenerParent(PWebSocketFrameListenerParent*) override;
 
   virtual mozilla::ipc::IProtocol*
   CloneProtocol(Channel* aChannel,
                 mozilla::ipc::ProtocolCloneContext* aCtx) override;
 
   virtual PDataChannelParent*
     AllocPDataChannelParent(const uint32_t& channelId) override;
   virtual bool DeallocPDataChannelParent(PDataChannelParent* parent) override;
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -7,16 +7,17 @@
 
 include protocol PContent;
 include protocol PHttpChannel;
 include protocol PCookieService;
 include protocol PBrowser;
 include protocol PWyciwygChannel;
 include protocol PFTPChannel;
 include protocol PWebSocket;
+include protocol PWebSocketFrameListener;
 include protocol PTCPSocket;
 include protocol PTCPServerSocket;
 include protocol PUDPSocket;
 include protocol PRemoteOpenFile;
 include protocol PDNSRequest;
 include protocol PChannelDiverter;
 include protocol PBlob; //FIXME: bug #792908
 include protocol PFileDescriptorSet;
@@ -40,16 +41,17 @@ namespace net {
 prio(normal upto urgent) sync protocol PNecko
 {
   manager PContent;
   manages PHttpChannel;
   manages PCookieService;
   manages PWyciwygChannel;
   manages PFTPChannel;
   manages PWebSocket;
+  manages PWebSocketFrameListener;
   manages PTCPSocket;
   manages PTCPServerSocket;
   manages PUDPSocket;
   manages PDNSRequest;
   manages PRemoteOpenFile;
   manages PDataChannel;
   manages PRtspController;
   manages PRtspChannel;
@@ -67,16 +69,17 @@ parent:
               FTPChannelCreationArgs args);
 
   PWebSocket(PBrowserOrId browser, SerializedLoadContext loadContext);
   PTCPServerSocket(uint16_t localPort, uint16_t backlog, bool useArrayBuffers);
   PUDPSocket(Principal principal, nsCString filter);
 
   PDNSRequest(nsCString hostName, uint32_t flags, nsCString networkInterface);
 
+  PWebSocketFrameListener(uint64_t aInnerWindowID);
 
   /* Predictor Methods */
   PredPredict(OptionalURIParams targetURI, OptionalURIParams sourceURI,
               uint32_t reason, SerializedLoadContext loadContext,
               bool hasVerifier);
   PredLearn(URIParams targetURI, OptionalURIParams sourceURI,
             uint32_t reason, SerializedLoadContext loadContext);
   PredReset();
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/PWebSocketFrameListener.ipdl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PNecko;
+
+using mozilla::net::WebSocketFrameData from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace net {
+
+async protocol PWebSocketFrameListener
+{
+  manager PNecko;
+
+child:
+  FrameReceived(uint32_t aWebSocketSerialID,
+                WebSocketFrameData aFrameData);
+
+  FrameSent(uint32_t aWebSocketSerialID,
+            WebSocketFrameData aFrameData);
+
+  __delete__();
+
+parent:
+  Close();
+};
+
+} //namespace net
+} //namespace mozilla
--- a/netwerk/protocol/websocket/WebSocketFrame.cpp
+++ b/netwerk/protocol/websocket/WebSocketFrame.cpp
@@ -16,30 +16,28 @@ namespace net {
 NS_INTERFACE_MAP_BEGIN(WebSocketFrame)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketFrame)
   NS_INTERFACE_MAP_ENTRY(nsIWebSocketFrame)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(WebSocketFrame)
 NS_IMPL_RELEASE(WebSocketFrame)
 
+WebSocketFrame::WebSocketFrame(const WebSocketFrameData& aData)
+  : mData(aData)
+{}
+
 WebSocketFrame::WebSocketFrame(bool aFinBit, bool aRsvBit1, bool aRsvBit2,
                                bool aRsvBit3, uint8_t aOpCode, bool aMaskBit,
                                uint32_t aMask, const nsCString& aPayload)
-  : mFinBit(aFinBit)
-  , mRsvBit1(aRsvBit1)
-  , mRsvBit2(aRsvBit2)
-  , mRsvBit3(aRsvBit3)
-  , mMaskBit(aMaskBit)
-  , mOpCode(aOpCode)
-  , mMask(aMask)
-  , mPayload(aPayload)
+  : mData(PR_Now(), aFinBit, aRsvBit1, aRsvBit2, aRsvBit3, aOpCode, aMaskBit,
+          aMask, aPayload)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-  mTimeStamp = PR_Now();
+  mData.mTimeStamp = PR_Now();
 }
 
 WebSocketFrame::~WebSocketFrame()
 {}
 
 #define WSF_GETTER( method, value , type )     \
 NS_IMETHODIMP                                  \
 WebSocketFrame::method(type* aValue)           \
@@ -47,29 +45,124 @@ WebSocketFrame::method(type* aValue)    
   MOZ_ASSERT(NS_IsMainThread());               \
   if (!aValue) {                               \
     return NS_ERROR_FAILURE;                   \
   }                                            \
   *aValue = value;                             \
   return NS_OK;                                \
 }
 
-WSF_GETTER(GetTimeStamp, mTimeStamp, DOMHighResTimeStamp);
-WSF_GETTER(GetFinBit, mFinBit, bool);
-WSF_GETTER(GetRsvBit1, mRsvBit1, bool);
-WSF_GETTER(GetRsvBit2, mRsvBit2, bool);
-WSF_GETTER(GetRsvBit3, mRsvBit3, bool);
-WSF_GETTER(GetOpCode, mOpCode, uint16_t);
-WSF_GETTER(GetMaskBit, mMaskBit, bool);
-WSF_GETTER(GetMask, mMask, uint32_t);
+WSF_GETTER(GetTimeStamp, mData.mTimeStamp, DOMHighResTimeStamp);
+WSF_GETTER(GetFinBit, mData.mFinBit, bool);
+WSF_GETTER(GetRsvBit1, mData.mRsvBit1, bool);
+WSF_GETTER(GetRsvBit2, mData.mRsvBit2, bool);
+WSF_GETTER(GetRsvBit3, mData.mRsvBit3, bool);
+WSF_GETTER(GetOpCode, mData.mOpCode, uint16_t);
+WSF_GETTER(GetMaskBit, mData.mMaskBit, bool);
+WSF_GETTER(GetMask, mData.mMask, uint32_t);
 
 #undef WSF_GETTER
 
 NS_IMETHODIMP
 WebSocketFrame::GetPayload(nsACString& aValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  aValue = mPayload;
+  aValue = mData.mPayload;
   return NS_OK;
 }
 
+WebSocketFrameData::WebSocketFrameData()
+  : mTimeStamp(0)
+  , mFinBit(false)
+  , mRsvBit1(false)
+  , mRsvBit2(false)
+  , mRsvBit3(false)
+  , mMaskBit(false)
+  , mOpCode(0)
+  , mMask(0)
+{
+  MOZ_COUNT_CTOR(WebSocketFrameData);
+}
+
+WebSocketFrameData::WebSocketFrameData(DOMHighResTimeStamp aTimeStamp,
+                                       bool aFinBit, bool aRsvBit1,
+                                       bool aRsvBit2, bool aRsvBit3,
+                                       uint8_t aOpCode, bool aMaskBit,
+                                       uint32_t aMask,
+                                       const nsCString& aPayload)
+  : mTimeStamp(aTimeStamp)
+  , mFinBit(aFinBit)
+  , mRsvBit1(aRsvBit1)
+  , mRsvBit2(aRsvBit2)
+  , mRsvBit3(aRsvBit3)
+  , mMaskBit(aMaskBit)
+  , mOpCode(aOpCode)
+  , mMask(aMask)
+  , mPayload(aPayload)
+{
+  MOZ_COUNT_CTOR(WebSocketFrameData);
+}
+
+WebSocketFrameData::WebSocketFrameData(const WebSocketFrameData& aData)
+  : mTimeStamp(aData.mTimeStamp)
+  , mFinBit(aData.mFinBit)
+  , mRsvBit1(aData.mRsvBit1)
+  , mRsvBit2(aData.mRsvBit2)
+  , mRsvBit3(aData.mRsvBit3)
+  , mMaskBit(aData.mMaskBit)
+  , mOpCode(aData.mOpCode)
+  , mMask(aData.mMask)
+  , mPayload(aData.mPayload)
+{
+  MOZ_COUNT_CTOR(WebSocketFrameData);
+}
+
+WebSocketFrameData::~WebSocketFrameData()
+{
+  MOZ_COUNT_DTOR(WebSocketFrameData);
+}
+
+void
+WebSocketFrameData::WriteIPCParams(IPC::Message* aMessage) const
+{
+  WriteParam(aMessage, mTimeStamp);
+  WriteParam(aMessage, mFinBit);
+  WriteParam(aMessage, mRsvBit1);
+  WriteParam(aMessage, mRsvBit2);
+  WriteParam(aMessage, mRsvBit3);
+  WriteParam(aMessage, mOpCode);
+  WriteParam(aMessage, mMaskBit);
+  WriteParam(aMessage, mMask);
+  WriteParam(aMessage, mPayload);
+}
+
+bool
+WebSocketFrameData::ReadIPCParams(const IPC::Message* aMessage,
+                                  void** aIter)
+{
+  if (!ReadParam(aMessage, aIter, &mTimeStamp)) {
+    return false;
+  }
+
+#define ReadParamHelper(x)                     \
+  {                                            \
+    bool bit;                                  \
+    if (!ReadParam(aMessage, aIter, &bit)) {   \
+      return false;                            \
+    }                                          \
+    x = bit;                                   \
+  }
+
+  ReadParamHelper(mFinBit);
+  ReadParamHelper(mRsvBit1);
+  ReadParamHelper(mRsvBit2);
+  ReadParamHelper(mRsvBit3);
+  ReadParamHelper(mMaskBit);
+
+#undef ReadParamHelper
+
+  return ReadParam(aMessage, aIter, &mOpCode) &&
+         ReadParam(aMessage, aIter, &mMask) &&
+         ReadParam(aMessage, aIter, &mPayload);
+}
+
 } // net namespace
 } // mozilla namespace
--- a/netwerk/protocol/websocket/WebSocketFrame.h
+++ b/netwerk/protocol/websocket/WebSocketFrame.h
@@ -4,43 +4,76 @@
  * 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_net_WebSocketFrame_h
 #define mozilla_net_WebSocketFrame_h
 
 #include "nsAutoPtr.h"
 #include "nsIWebSocketFrameService.h"
+#include "nsString.h"
+
+namespace IPC {
+class Message;
+}
 
 namespace mozilla {
 namespace net {
 
-class WebSocketFrame final : public nsIWebSocketFrame
+class WebSocketFrameData final
 {
 public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIWEBSOCKETFRAME
+  WebSocketFrameData();
+
+  explicit WebSocketFrameData(const WebSocketFrameData& aData);
 
-  WebSocketFrame(bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3,
-                 uint8_t aOpCode, bool aMaskBit, uint32_t aMask,
-                 const nsCString& aPayload);
+  WebSocketFrameData(DOMHighResTimeStamp aTimeStamp, bool aFinBit,
+                     bool aRsvBit1, bool aRsvBit2, bool aRsvBit3,
+                     uint8_t aOpCode, bool aMaskBit, uint32_t aMask,
+                     const nsCString& aPayload);
 
-private:
-  ~WebSocketFrame();
+  ~WebSocketFrameData();
+
+  // For IPC serialization
+  void WriteIPCParams(IPC::Message* aMessage) const;
+  bool ReadIPCParams(const IPC::Message* aMessage, void** aIter);
 
   DOMHighResTimeStamp mTimeStamp;
 
   bool mFinBit : 1;
   bool mRsvBit1 : 1;
   bool mRsvBit2 : 1;
   bool mRsvBit3 : 1;
   bool mMaskBit : 1;
   uint8_t mOpCode;
 
   uint32_t mMask;
 
   nsCString mPayload;
 };
 
+class WebSocketFrame final : public nsIWebSocketFrame
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIWEBSOCKETFRAME
+
+  explicit WebSocketFrame(const WebSocketFrameData& aData);
+
+  WebSocketFrame(bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3,
+                 uint8_t aOpCode, bool aMaskBit, uint32_t aMask,
+                 const nsCString& aPayload);
+
+  const WebSocketFrameData& Data() const
+  {
+    return mData;
+  }
+
+private:
+  ~WebSocketFrame();
+
+  WebSocketFrameData mData;
+};
+
 } // net namespace
 } // mozilla namespace
 
 #endif // mozilla_net_WebSocketFrame_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/WebSocketFrameListenerChild.cpp
@@ -0,0 +1,63 @@
+/* -*- 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 "WebSocketFrameListenerChild.h"
+
+#include "WebSocketFrame.h"
+#include "WebSocketFrameService.h"
+
+namespace mozilla {
+namespace net {
+
+WebSocketFrameListenerChild::WebSocketFrameListenerChild(uint64_t aInnerWindowID)
+  : mService(WebSocketFrameService::GetOrCreate())
+  , mInnerWindowID(aInnerWindowID)
+{}
+
+WebSocketFrameListenerChild::~WebSocketFrameListenerChild()
+{
+  MOZ_ASSERT(!mService);
+}
+
+bool
+WebSocketFrameListenerChild::RecvFrameReceived(const uint32_t& aWebSocketSerialID,
+                                               const WebSocketFrameData& aFrameData)
+{
+  if (mService) {
+    RefPtr<WebSocketFrame> frame = new WebSocketFrame(aFrameData);
+    mService->FrameReceived(aWebSocketSerialID, mInnerWindowID, frame);
+  }
+
+  return true;
+}
+
+bool
+WebSocketFrameListenerChild::RecvFrameSent(const uint32_t& aWebSocketSerialID,
+                                           const WebSocketFrameData& aFrameData)
+{
+  if (mService) {
+    RefPtr<WebSocketFrame> frame = new WebSocketFrame(aFrameData);
+    mService->FrameSent(aWebSocketSerialID, mInnerWindowID, frame);
+  }
+
+  return true;
+}
+
+void
+WebSocketFrameListenerChild::Close()
+{
+  mService = nullptr;
+  SendClose();
+}
+
+void
+WebSocketFrameListenerChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mService = nullptr;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/WebSocketFrameListenerChild.h
@@ -0,0 +1,44 @@
+/* -*- 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_net_WebSocketFrameListenerChild_h
+#define mozilla_net_WebSocketFrameListenerChild_h
+
+#include "mozilla/net/PWebSocketFrameListenerChild.h"
+
+namespace mozilla {
+namespace net {
+
+class WebSocketFrameService;
+
+class WebSocketFrameListenerChild final : public PWebSocketFrameListenerChild
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(WebSocketFrameListenerChild)
+
+  explicit WebSocketFrameListenerChild(uint64_t aInnerWindowID);
+
+  bool RecvFrameReceived(const uint32_t& aWebSocketSerialID,
+                         const WebSocketFrameData& aFrameData) override;
+
+  bool RecvFrameSent(const uint32_t& aWebSocketSerialID,
+                     const WebSocketFrameData& aFrameData) override;
+
+  void Close();
+
+private:
+  ~WebSocketFrameListenerChild();
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  RefPtr<WebSocketFrameService> mService;
+  uint64_t mInnerWindowID;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_WebSocketFrameListenerChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/WebSocketFrameListenerParent.cpp
@@ -0,0 +1,86 @@
+/* -*- 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 "WebSocketFrameListenerParent.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+namespace net {
+
+NS_INTERFACE_MAP_BEGIN(WebSocketFrameListenerParent)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketFrameListener)
+  NS_INTERFACE_MAP_ENTRY(nsIWebSocketFrameListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(WebSocketFrameListenerParent)
+NS_IMPL_RELEASE(WebSocketFrameListenerParent)
+
+WebSocketFrameListenerParent::WebSocketFrameListenerParent(uint64_t aInnerWindowID)
+  : mService(WebSocketFrameService::GetOrCreate())
+  , mInnerWindowID(aInnerWindowID)
+{
+  mService->AddListener(mInnerWindowID, this);
+}
+
+WebSocketFrameListenerParent::~WebSocketFrameListenerParent()
+{
+  MOZ_ASSERT(!mService);
+}
+
+bool
+WebSocketFrameListenerParent::RecvClose()
+{
+  if (mService) {
+    UnregisterListener();
+    unused << Send__delete__(this);
+  }
+
+  return true;
+}
+
+void
+WebSocketFrameListenerParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  UnregisterListener();
+}
+
+void
+WebSocketFrameListenerParent::UnregisterListener()
+{
+  if (mService) {
+    mService->RemoveListener(mInnerWindowID, this);
+    mService = nullptr;
+  }
+}
+
+NS_IMETHODIMP
+WebSocketFrameListenerParent::FrameReceived(uint32_t aWebSocketSerialID,
+                                            nsIWebSocketFrame* aFrame)
+{
+  if (!aFrame) {
+    return NS_ERROR_FAILURE;
+  }
+
+  WebSocketFrame* frame = static_cast<WebSocketFrame*>(aFrame);
+  unused << SendFrameReceived(aWebSocketSerialID, frame->Data());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebSocketFrameListenerParent::FrameSent(uint32_t aWebSocketSerialID,
+                                        nsIWebSocketFrame* aFrame)
+{
+  if (!aFrame) {
+    return NS_ERROR_FAILURE;
+  }
+
+  WebSocketFrame* frame = static_cast<WebSocketFrame*>(aFrame);
+  unused << SendFrameSent(aWebSocketSerialID, frame->Data());
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/WebSocketFrameListenerParent.h
@@ -0,0 +1,43 @@
+/* -*- 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_net_WebSocketFrameListenerParent_h
+#define mozilla_net_WebSocketFrameListenerParent_h
+
+#include "mozilla/net/PWebSocketFrameListenerParent.h"
+#include "nsIWebSocketFrameService.h"
+
+namespace mozilla {
+namespace net {
+
+class WebSocketFrameService;
+
+class WebSocketFrameListenerParent final : public PWebSocketFrameListenerParent
+                                         , public nsIWebSocketFrameListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWEBSOCKETFRAMELISTENER
+
+  explicit WebSocketFrameListenerParent(uint64_t aInnerWindowID);
+
+private:
+  ~WebSocketFrameListenerParent();
+
+  virtual bool RecvClose() override;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  void UnregisterListener();
+
+  RefPtr<WebSocketFrameService> mService;
+  uint64_t mInnerWindowID;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_WebSocketFrameListenerParent_h
--- a/netwerk/protocol/websocket/WebSocketFrameService.cpp
+++ b/netwerk/protocol/websocket/WebSocketFrameService.cpp
@@ -1,30 +1,38 @@
 /* -*- 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 "WebSocketFrame.h"
+#include "WebSocketFrameListenerChild.h"
 #include "WebSocketFrameService.h"
 
+#include "mozilla/net/NeckoChild.h"
 #include "mozilla/StaticPtr.h"
 #include "nsISupportsPrimitives.h"
 #include "nsXULAppAPI.h"
 
 extern PRThread *gSocketThread;
 
 namespace mozilla {
 namespace net {
 
 namespace {
 
 StaticRefPtr<WebSocketFrameService> gWebSocketFrameService;
 
+bool
+IsChildProcess()
+{
+  return XRE_GetProcessType() != GeckoProcessType_Default;
+}
+
 } // anonymous namespace
 
 class WebSocketFrameRunnable final : public nsRunnable
 {
 public:
   WebSocketFrameRunnable(uint32_t aWebSocketSerialID,
                          uint64_t aInnerWindowID,
                          WebSocketFrame* aFrame,
@@ -77,17 +85,16 @@ protected:
 
   bool mFrameSent;
 };
 
 /* static */ already_AddRefed<WebSocketFrameService>
 WebSocketFrameService::GetOrCreate()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(XRE_IsParentProcess());
 
   if (!gWebSocketFrameService) {
     gWebSocketFrameService = new WebSocketFrameService();
   }
 
   RefPtr<WebSocketFrameService> service = gWebSocketFrameService.get();
   return service.forget();
 }
@@ -100,17 +107,16 @@ NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(WebSocketFrameService)
 NS_IMPL_RELEASE(WebSocketFrameService)
 
 WebSocketFrameService::WebSocketFrameService()
   : mCountListeners(0)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(XRE_IsParentProcess());
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->AddObserver(this, "xpcom-shutdown", false);
     obs->AddObserver(this, "inner-window-destroyed", false);
   }
 }
 
@@ -121,19 +127,16 @@ WebSocketFrameService::~WebSocketFrameSe
 
 void
 WebSocketFrameService::FrameReceived(uint32_t aWebSocketSerialID,
                                      uint64_t aInnerWindowID,
                                      WebSocketFrame* aFrame)
 {
   MOZ_ASSERT(aFrame);
 
-  // This method can be called only from a the network thread.
-  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-
   // Let's continue only if we have some listeners.
   if (!HasListeners()) {
     return;
   }
 
   RefPtr<WebSocketFrameRunnable> runnable =
     new WebSocketFrameRunnable(aWebSocketSerialID, aInnerWindowID,
                                aFrame, false /* frameSent */);
@@ -143,19 +146,16 @@ WebSocketFrameService::FrameReceived(uin
 
 void
 WebSocketFrameService::FrameSent(uint32_t aWebSocketSerialID,
                                  uint64_t aInnerWindowID,
                                  WebSocketFrame* aFrame)
 {
   MOZ_ASSERT(aFrame);
 
-  // This method can be called only from a the network thread.
-  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-
   // Let's continue only if we have some listeners.
   if (!HasListeners()) {
     return;
   }
 
   RefPtr<WebSocketFrameRunnable> runnable =
     new WebSocketFrameRunnable(aWebSocketSerialID, aInnerWindowID,
                                aFrame, true /* frameSent */);
@@ -171,47 +171,61 @@ WebSocketFrameService::AddListener(uint6
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!aListener) {
     return NS_ERROR_FAILURE;
   }
 
   ++mCountListeners;
 
-  WindowListeners* listeners = mWindows.Get(aInnerWindowID);
-  if (!listeners) {
-    listeners = new WindowListeners();
-    mWindows.Put(aInnerWindowID, listeners);
+  WindowListener* listener = mWindows.Get(aInnerWindowID);
+  if (!listener) {
+    listener = new WindowListener();
+
+    if (IsChildProcess()) {
+      PWebSocketFrameListenerChild* actor =
+        gNeckoChild->SendPWebSocketFrameListenerConstructor(aInnerWindowID);
+
+      listener->mActor = static_cast<WebSocketFrameListenerChild*>(actor);
+      MOZ_ASSERT(listener->mActor);
+    }
+
+    mWindows.Put(aInnerWindowID, listener);
   }
 
-  listeners->AppendElement(aListener);
+  listener->mListeners.AppendElement(aListener);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketFrameService::RemoveListener(uint64_t aInnerWindowID,
                                       nsIWebSocketFrameListener* aListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!aListener) {
     return NS_ERROR_FAILURE;
   }
 
-  WindowListeners* listeners = mWindows.Get(aInnerWindowID);
-  if (!listeners) {
+  WindowListener* listener = mWindows.Get(aInnerWindowID);
+  if (!listener) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!listeners->RemoveElement(aListener)) {
+  if (!listener->mListeners.RemoveElement(aListener)) {
     return NS_ERROR_FAILURE;
   }
 
   // The last listener for this window.
-  if (listeners->IsEmpty()) {
+  if (listener->mListeners.IsEmpty()) {
+    if (IsChildProcess()) {
+      ShutdownActorListener(listener);
+    }
+
     mWindows.Remove(aInnerWindowID);
   }
 
   MOZ_ASSERT(mCountListeners);
   --mCountListeners;
 
   return NS_OK;
 }
@@ -230,23 +244,27 @@ WebSocketFrameService::Observe(nsISuppor
   if (!strcmp(aTopic, "inner-window-destroyed") && HasListeners()) {
     nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
     NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
 
     uint64_t innerID;
     nsresult rv = wrapper->GetData(&innerID);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    WindowListeners* listeners = mWindows.Get(innerID);
-    if (!listeners) {
+    WindowListener* listener = mWindows.Get(innerID);
+    if (!listener) {
       return NS_OK;
     }
 
-    MOZ_ASSERT(mCountListeners >= listeners->Length());
-    mCountListeners -= listeners->Length();
+    MOZ_ASSERT(mCountListeners >= listener->mListeners.Length());
+    mCountListeners -= listener->mListeners.Length();
+
+    if (IsChildProcess()) {
+      ShutdownActorListener(listener);
+    }
 
     mWindows.Remove(innerID);
   }
 
   // This should not happen.
   return NS_ERROR_FAILURE;
 }
 
@@ -271,17 +289,27 @@ bool
 WebSocketFrameService::HasListeners() const
 {
   return !!mCountListeners;
 }
 
 WebSocketFrameService::WindowListeners*
 WebSocketFrameService::GetListeners(uint64_t aInnerWindowID) const
 {
-  return mWindows.Get(aInnerWindowID);
+  WindowListener* listener = mWindows.Get(aInnerWindowID);
+  return listener ? &listener->mListeners : nullptr;
+}
+
+void
+WebSocketFrameService::ShutdownActorListener(WindowListener* aListener)
+{
+  MOZ_ASSERT(aListener);
+  MOZ_ASSERT(aListener->mActor);
+  aListener->mActor->Close();
+  aListener->mActor = nullptr;
 }
 
 WebSocketFrame*
 WebSocketFrameService::CreateFrameIfNeeded(bool aFinBit, bool aRsvBit1,
                                            bool aRsvBit2, bool aRsvBit3,
                                            uint8_t aOpCode, bool aMaskBit,
                                            uint32_t aMask,
                                            const nsCString& aPayload)
--- a/netwerk/protocol/websocket/WebSocketFrameService.h
+++ b/netwerk/protocol/websocket/WebSocketFrameService.h
@@ -16,16 +16,17 @@
 #include "nsIObserver.h"
 #include "nsISupportsImpl.h"
 #include "nsTObserverArray.h"
 
 namespace mozilla {
 namespace net {
 
 class WebSocketFrame;
+class WebSocketFrameListenerChild;
 
 class WebSocketFrameService final : public nsIWebSocketFrameService
                                   , public nsIObserver
 {
   friend class WebSocketFrameRunnable;
 
 public:
   NS_DECL_ISUPPORTS
@@ -62,20 +63,27 @@ private:
   WebSocketFrameService();
   ~WebSocketFrameService();
 
   bool HasListeners() const;
   void Shutdown();
 
   typedef nsTObserverArray<nsCOMPtr<nsIWebSocketFrameListener>> WindowListeners;
 
+  struct WindowListener
+  {
+    WindowListeners mListeners;
+    RefPtr<WebSocketFrameListenerChild> mActor;
+  };
+
   WindowListeners* GetListeners(uint64_t aInnerWindowID) const;
+  void ShutdownActorListener(WindowListener* aListener);
 
   // Used only on the main-thread.
-  nsClassHashtable<nsUint64HashKey, WindowListeners> mWindows;
+  nsClassHashtable<nsUint64HashKey, WindowListener> mWindows;
 
   Atomic<uint64_t> mCountListeners;
 };
 
 } // net namespace
 } // mozilla namespace
 
 #endif // mozilla_net_WebSocketFrameService_h
--- a/netwerk/protocol/websocket/moz.build
+++ b/netwerk/protocol/websocket/moz.build
@@ -12,30 +12,36 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'necko_websocket'
 
 EXPORTS.mozilla.net += [
     'BaseWebSocketChannel.h',
     'WebSocketChannel.h',
     'WebSocketChannelChild.h',
     'WebSocketChannelParent.h',
+    'WebSocketFrame.h',
+    'WebSocketFrameListenerChild.h',
+    'WebSocketFrameListenerParent.h',
     'WebSocketFrameService.h',
 ]
 
 UNIFIED_SOURCES += [
     'BaseWebSocketChannel.cpp',
     'WebSocketChannel.cpp',
     'WebSocketChannelChild.cpp',
     'WebSocketChannelParent.cpp',
     'WebSocketFrame.cpp',
+    'WebSocketFrameListenerChild.cpp',
+    'WebSocketFrameListenerParent.cpp',
     'WebSocketFrameService.cpp',
 ]
 
 IPDL_SOURCES += [
     'PWebSocket.ipdl',
+    'PWebSocketFrameListener.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/dom/base',