Bug 950660: Part 4: Bridge TCPSocketChild to nr_socket r=bwc,jdm
author"Chih-Kai (Patrick) Wang" <kk1fff@ckwang.info>
Mon, 05 Jan 2015 15:49:50 +0800
changeset 294589 8eda15c59d265f755582342dd027e97959b2429b
parent 294588 dfaa6b689bc00d216add359d8ca8a8e40c65a109
child 294590 41cce993981073d8319cab239570192c3aacd3d1
push id5619
push userj.parkouss@gmail.com
push dateMon, 21 Sep 2015 17:26:04 +0000
reviewersbwc, jdm
bugs950660
milestone43.0a1
Bug 950660: Part 4: Bridge TCPSocketChild to nr_socket r=bwc,jdm Improve use of TCPSocket to track in-flight writes and suppress extra runnables Adds lots of logging to nr_socket_buffered_stun.c Rework mtransport code to use new TCPSocketChild interface
media/mtransport/build/moz.build
media/mtransport/moz.build
media/mtransport/nr_socket_prsock.cpp
media/mtransport/nr_socket_prsock.h
media/mtransport/standalone/moz.build
media/mtransport/testlib/moz.build
media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
--- a/media/mtransport/build/moz.build
+++ b/media/mtransport/build/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+include("/ipc/chromium/chromium-config.mozbuild")
+
 EXPORTS.mtransport += [
     '../dtlsidentity.h',
     '../m_cpp_utils.h',
     '../nricectx.h',
     '../nricemediastream.h',
     '../nriceresolverfake.h',
     '../rlogringbuffer.h',
     '../runnable_utils.h',
--- a/media/mtransport/moz.build
+++ b/media/mtransport/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+include("/ipc/chromium/chromium-config.mozbuild")
+
 DIRS += [
     '/media/mtransport/third_party',
     '/media/mtransport/build',
     '/media/mtransport/testlib',
 ]
 if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     DIRS += [
         '/media/mtransport/standalone',
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -101,16 +101,64 @@ nrappkit copyright:
 #include "nsISocketTransportService.h"
 #include "nsNetCID.h"
 #include "nsISupportsImpl.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXPCOM.h"
 #include "nsXULAppAPI.h"
 #include "runnable_utils.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsTArray.h"
+#include "mozilla/dom/TCPSocketBinding.h"
+#include "nsITCPSocketCallback.h"
+
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+// csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING
+#ifdef LOG_INFO
+#define LOG_TEMP_INFO LOG_INFO
+#undef LOG_INFO
+#endif
+#ifdef LOG_WARNING
+#define LOG_TEMP_WARNING LOG_WARNING
+#undef LOG_WARNING
+#endif
+#if defined(LOG_DEBUG)
+#define LOG_TEMP_DEBUG LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+#undef strlcpy
+
+// TCPSocketChild.h doesn't include TypedArray.h
+namespace mozilla {
+namespace dom {
+class ArrayBuffer;
+}
+}
+#include "mozilla/dom/network/TCPSocketChild.h"
+
+#ifdef LOG_TEMP_INFO
+#define LOG_INFO LOG_TEMP_INFO
+#endif
+#ifdef LOG_TEMP_WARNING
+#define LOG_WARNING LOG_TEMP_WARNING
+#endif
+
+#ifdef LOG_TEMP_DEBUG
+#define LOG_DEBUG LOG_TEMP_DEBUG
+#endif
+#ifdef XP_WIN
+#ifdef LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+// cloned from csi_platform.h.  Win32 doesn't like how we hide symbols
+#define LOG_DEBUG 7
+#endif
+#endif
+
 
 extern "C" {
 #include "nr_api.h"
 #include "async_wait.h"
 #include "nr_socket.h"
 #include "nr_socket_local.h"
 #include "stun_hint.h"
 }
@@ -198,16 +246,41 @@ private:
 static StaticRefPtr<SingletonThreadHolder> sThread;
 
 static void ClearSingletonOnShutdown()
 {
   ClearOnShutdown(&sThread);
 }
 #endif
 
+static nsIThread* GetIOThreadAndAddUse_s()
+{
+  // Always runs on STS thread!
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+  // We need to safely release this on shutdown to avoid leaks
+  if (!sThread) {
+    sThread = new SingletonThreadHolder(NS_LITERAL_CSTRING("mtransport"));
+    NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
+  }
+  // Mark that we're using the shared thread and need it to stick around
+  sThread->AddUse();
+  return sThread->GetThread();
+#else
+  static nsCOMPtr<nsIThread> sThread;
+  if (!sThread) {
+    (void) NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
+  }
+  return sThread;
+#endif
+}
+
+NrSocketIpc::NrSocketIpc(nsIEventTarget *aThread)
+  : io_thread_(aThread)
+{}
+
 static TimeStamp nr_socket_short_term_violation_time;
 static TimeStamp nr_socket_long_term_violation_time;
 
 TimeStamp NrSocketBase::short_term_violation_time() {
   return nr_socket_short_term_violation_time;
 }
 
 TimeStamp NrSocketBase::long_term_violation_time() {
@@ -919,133 +992,111 @@ int NrSocket::accept(nr_transport_addr *
 abort:
   if (_status) {
     delete sock;
   }
 
   return(_status);
 }
 
-NS_IMPL_ISUPPORTS(NrSocketIpcProxy, nsIUDPSocketInternal)
+NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy, nsIUDPSocketInternal)
 
 nsresult
-NrSocketIpcProxy::Init(const nsRefPtr<NrSocketIpc>& socket)
+NrUdpSocketIpcProxy::Init(const nsRefPtr<NrUdpSocketIpc>& socket)
 {
   nsresult rv;
   sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     MOZ_ASSERT(false, "Failed to get STS thread");
     return rv;
   }
 
   socket_ = socket;
   return NS_OK;
 }
 
-NrSocketIpcProxy::~NrSocketIpcProxy()
+NrUdpSocketIpcProxy::~NrUdpSocketIpcProxy()
 {
   // Send our ref to STS to be released
   RUN_ON_THREAD(sts_thread_,
                 mozilla::WrapRelease(socket_.forget()),
                 NS_DISPATCH_NORMAL);
 }
 
 // IUDPSocketInternal interfaces
 // callback while error happened in UDP socket operation
-NS_IMETHODIMP NrSocketIpcProxy::CallListenerError(const nsACString &message,
-                                                  const nsACString &filename,
-                                                  uint32_t line_number) {
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerError(const nsACString &message,
+                                                     const nsACString &filename,
+                                                     uint32_t line_number) {
   return socket_->CallListenerError(message, filename, line_number);
 }
 
 // callback while receiving UDP packet
-NS_IMETHODIMP NrSocketIpcProxy::CallListenerReceivedData(const nsACString &host,
-                                                         uint16_t port,
-                                                         const uint8_t *data,
-                                                         uint32_t data_length) {
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerReceivedData(const nsACString &host,
+                                                            uint16_t port,
+                                                            const uint8_t *data,
+                                                            uint32_t data_length) {
   return socket_->CallListenerReceivedData(host, port, data, data_length);
 }
 
 // callback while UDP socket is opened
-NS_IMETHODIMP NrSocketIpcProxy::CallListenerOpened() {
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerOpened() {
   return socket_->CallListenerOpened();
 }
 
 // callback while UDP socket is closed
-NS_IMETHODIMP NrSocketIpcProxy::CallListenerClosed() {
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerClosed() {
   return socket_->CallListenerClosed();
 }
 
-// NrSocketIpc Implementation
-NrSocketIpc::NrSocketIpc()
-    : err_(false),
-      state_(NR_INIT),
-      io_thread_(GetIOThreadAndAddUse_s()),
-      monitor_("NrSocketIpc") {
+// NrUdpSocketIpc Implementation
+NrUdpSocketIpc::NrUdpSocketIpc()
+  : NrSocketIpc(GetIOThreadAndAddUse_s()),
+    monitor_("NrUdpSocketIpc"),
+    err_(false),
+    state_(NR_INIT) {
 }
 
-NrSocketIpc::~NrSocketIpc()
+NrUdpSocketIpc::~NrUdpSocketIpc()
 {
   // also guarantees socket_child_ is released from the io_thread, and
   // tells the SingletonThreadHolder we're done with it
 
 #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
   // close(), but transfer the socket_child_ reference to die as well
   RUN_ON_THREAD(io_thread_,
-                mozilla::WrapRunnableNM(&NrSocketIpc::release_child_i,
+                mozilla::WrapRunnableNM(&NrUdpSocketIpc::release_child_i,
                                         socket_child_.forget().take(),
                                         sts_thread_),
                 NS_DISPATCH_NORMAL);
 #endif
 }
 
-/* static */
-nsIThread* NrSocketIpc::GetIOThreadAndAddUse_s()
-{
-  // Always runs on STS thread!
-#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
-  // We need to safely release this on shutdown to avoid leaks
-  if (!sThread) {
-    sThread = new SingletonThreadHolder(NS_LITERAL_CSTRING("mtransport"));
-    NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
-  }
-  // Mark that we're using the shared thread and need it to stick around
-  sThread->AddUse();
-  return sThread->GetThread();
-#else
-  static nsCOMPtr<nsIThread> sThread;
-  if (!sThread) {
-    (void) NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
-  }
-  return sThread;
-#endif
-}
-
 // IUDPSocketInternal interfaces
 // callback while error happened in UDP socket operation
-NS_IMETHODIMP NrSocketIpc::CallListenerError(const nsACString &message,
-                                             const nsACString &filename,
-                                             uint32_t line_number) {
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerError(const nsACString &message,
+                                                const nsACString &filename,
+                                                uint32_t line_number) {
   ASSERT_ON_THREAD(io_thread_);
 
   r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d",
         message.BeginReading(), filename.BeginReading(), line_number );
 
   ReentrantMonitorAutoEnter mon(monitor_);
   err_ = true;
   monitor_.NotifyAll();
 
   return NS_OK;
 }
 
 // callback while receiving UDP packet
-NS_IMETHODIMP NrSocketIpc::CallListenerReceivedData(const nsACString &host,
-                                                    uint16_t port,
-                                                    const uint8_t *data,
-                                                    uint32_t data_length) {
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerReceivedData(const nsACString &host,
+                                                       uint16_t port,
+                                                       const uint8_t *data,
+                                                       uint32_t data_length) {
   ASSERT_ON_THREAD(io_thread_);
 
   PRNetAddr addr;
   memset(&addr, 0, sizeof(addr));
 
   {
     ReentrantMonitorAutoEnter mon(monitor_);
 
@@ -1062,25 +1113,25 @@ NS_IMETHODIMP NrSocketIpc::CallListenerR
       return NS_OK;
     }
   }
 
   nsAutoPtr<DataBuffer> buf(new DataBuffer(data, data_length));
   RefPtr<nr_udp_message> msg(new nr_udp_message(addr, buf));
 
   RUN_ON_THREAD(sts_thread_,
-                mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::recv_callback_s,
+                mozilla::WrapRunnable(nsRefPtr<NrUdpSocketIpc>(this),
+                                      &NrUdpSocketIpc::recv_callback_s,
                                       msg),
                 NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 // callback while UDP socket is opened
-NS_IMETHODIMP NrSocketIpc::CallListenerOpened() {
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerOpened() {
   ASSERT_ON_THREAD(io_thread_);
   ReentrantMonitorAutoEnter mon(monitor_);
 
   uint16_t port;
   if (NS_FAILED(socket_child_->GetLocalPort(&port))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to get local port");
     return NS_OK;
@@ -1124,68 +1175,64 @@ NS_IMETHODIMP NrSocketIpc::CallListenerO
   }
 
   mon.NotifyAll();
 
   return NS_OK;
 }
 
 // callback while UDP socket is closed
-NS_IMETHODIMP NrSocketIpc::CallListenerClosed() {
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerClosed() {
   ASSERT_ON_THREAD(io_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING);
   state_ = NR_CLOSED;
 
   return NS_OK;
 }
 
-// nr_socket public APIs
-int NrSocketIpc::create(nr_transport_addr *addr) {
+//
+// NrSocketBase methods.
+//
+int NrUdpSocketIpc::create(nr_transport_addr *addr) {
   ASSERT_ON_THREAD(sts_thread_);
 
   int r, _status;
   nsresult rv;
   int32_t port;
   nsCString host;
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   if (state_ != NR_INIT) {
     ABORT(R_INTERNAL);
   }
 
-  // Bug 950660: Remote TCP socket is not supported yet.
-  if (NS_WARN_IF(addr->protocol != IPPROTO_UDP)) {
-    MOZ_ASSERT(false, "NrSocket over TCP is not e10s ready, see Bug 950660");
-    ABORT(R_INTERNAL);
-  }
-
   sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     MOZ_ASSERT(false, "Failed to get STS thread");
     ABORT(R_INTERNAL);
   }
 
   if ((r=nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
     ABORT(r);
   }
 
-  // wildcard address will be resolved at NrSocketIpc::CallListenerVoid
+  // wildcard address will be resolved at NrUdpSocketIpc::CallListenerVoid
   if ((r=nr_transport_addr_copy(&my_addr_, addr))) {
     ABORT(r);
   }
 
   state_ = NR_CONNECTING;
 
   RUN_ON_THREAD(io_thread_,
-                mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::create_i,
+                mozilla::WrapRunnable(nsRefPtr<NrUdpSocketIpc>(this),
+                                      &NrUdpSocketIpc::create_i,
                                       host, static_cast<uint16_t>(port)),
                 NS_DISPATCH_NORMAL);
 
   // Wait until socket creation complete.
   mon.Wait();
 
   if (err_) {
     ABORT(R_INTERNAL);
@@ -1193,17 +1240,17 @@ int NrSocketIpc::create(nr_transport_add
 
   state_ = NR_CONNECTED;
 
   _status = 0;
 abort:
   return(_status);
 }
 
-int NrSocketIpc::sendto(const void *msg, size_t len, int flags,
+int NrUdpSocketIpc::sendto(const void *msg, size_t len, int flags,
                         nr_transport_addr *to) {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   //If send err happened before, simply return the error.
   if (err_) {
     return R_IO_ERROR;
@@ -1217,40 +1264,40 @@ int NrSocketIpc::sendto(const void *msg,
   net::NetAddr addr;
   if ((r=nr_transport_addr_to_netaddr(to, &addr))) {
     return r;
   }
 
   nsAutoPtr<DataBuffer> buf(new DataBuffer(static_cast<const uint8_t*>(msg), len));
 
   RUN_ON_THREAD(io_thread_,
-                mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::sendto_i,
+                mozilla::WrapRunnable(nsRefPtr<NrUdpSocketIpc>(this),
+                                      &NrUdpSocketIpc::sendto_i,
                                       addr, buf),
                 NS_DISPATCH_NORMAL);
   return 0;
 }
 
-void NrSocketIpc::close() {
+void NrUdpSocketIpc::close() {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
   state_ = NR_CLOSING;
 
   RUN_ON_THREAD(io_thread_,
-                mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::close_i),
+                mozilla::WrapRunnable(nsRefPtr<NrUdpSocketIpc>(this),
+                                      &NrUdpSocketIpc::close_i),
                 NS_DISPATCH_NORMAL);
 
   //remove all enqueued messages
   std::queue<RefPtr<nr_udp_message> > empty;
   std::swap(received_msgs_, empty);
 }
 
-int NrSocketIpc::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
+int NrUdpSocketIpc::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
                           nr_transport_addr *from) {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   int r, _status;
   uint32_t consumed_len;
 
@@ -1284,77 +1331,78 @@ int NrSocketIpc::recvfrom(void *buf, siz
     *len = consumed_len;
   }
 
   _status = 0;
 abort:
   return(_status);
 }
 
-int NrSocketIpc::getaddr(nr_transport_addr *addrp) {
+int NrUdpSocketIpc::getaddr(nr_transport_addr *addrp) {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   if (state_ != NR_CONNECTED) {
     return R_INTERNAL;
   }
 
   return nr_transport_addr_copy(addrp, &my_addr_);
 }
 
-int NrSocketIpc::connect(nr_transport_addr *addr) {
+int NrUdpSocketIpc::connect(nr_transport_addr *addr) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-int NrSocketIpc::write(const void *msg, size_t len, size_t *written) {
+int NrUdpSocketIpc::write(const void *msg, size_t len, size_t *written) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-int NrSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
+int NrUdpSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-int NrSocketIpc::listen(int backlog) {
+int NrUdpSocketIpc::listen(int backlog) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-int NrSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
+int NrUdpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
 // IO thread executors
-void NrSocketIpc::create_i(const nsACString &host, const uint16_t port) {
+void NrUdpSocketIpc::create_i(const nsACString &host, const uint16_t port) {
   ASSERT_ON_THREAD(io_thread_);
 
   nsresult rv;
   nsCOMPtr<nsIUDPSocketChild> socketChild = do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
   if (NS_FAILED(rv)) {
+    ReentrantMonitorAutoEnter mon(monitor_);
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDPSocketChild");
     return;
   }
 
   // This can spin the event loop; don't do that with the monitor held
   socketChild->SetBackgroundSpinsEvents();
 
   ReentrantMonitorAutoEnter mon(monitor_);
   if (!socket_child_) {
     socket_child_ = socketChild;
     socket_child_->SetFilterName(nsCString("stun"));
   } else {
     socketChild = nullptr;
   }
 
-  nsRefPtr<NrSocketIpcProxy> proxy(new NrSocketIpcProxy);
+  nsRefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
   rv = proxy->Init(this);
   if (NS_FAILED(rv)) {
     err_ = true;
     mon.NotifyAll();
     return;
   }
 
   // XXX bug 1126232 - don't use null Principal!
@@ -1363,65 +1411,64 @@ void NrSocketIpc::create_i(const nsACStr
                                     /* loopback = */ false))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDP socket");
     mon.NotifyAll();
     return;
   }
 }
 
-void NrSocketIpc::sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
+void NrUdpSocketIpc::sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
   ASSERT_ON_THREAD(io_thread_);
 
+  ReentrantMonitorAutoEnter mon(monitor_);
+
   if (!socket_child_) {
     MOZ_ASSERT(false);
     err_ = true;
     return;
   }
-
-  ReentrantMonitorAutoEnter mon(monitor_);
-
   if (NS_FAILED(socket_child_->SendWithAddress(&addr,
                                                buf->data(),
                                                buf->len()))) {
     err_ = true;
   }
 }
 
-void NrSocketIpc::close_i() {
+void NrUdpSocketIpc::close_i() {
   ASSERT_ON_THREAD(io_thread_);
 
   if (socket_child_) {
     socket_child_->Close();
     socket_child_ = nullptr;
   }
 }
 
 #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
 // close(), but transfer the socket_child_ reference to die as well
 // static
-void NrSocketIpc::release_child_i(nsIUDPSocketChild* aChild,
-                                  nsCOMPtr<nsIEventTarget> sts_thread) {
+void NrUdpSocketIpc::release_child_i(nsIUDPSocketChild* aChild,
+                                     nsCOMPtr<nsIEventTarget> sts_thread) {
   nsRefPtr<nsIUDPSocketChild> socket_child_ref =
     already_AddRefed<nsIUDPSocketChild>(aChild);
   if (socket_child_ref) {
     socket_child_ref->Close();
   }
   // Tell SingletonThreadHolder we're done with it
   RUN_ON_THREAD(sts_thread,
-                mozilla::WrapRunnableNM(&NrSocketIpc::release_use_s),
+                mozilla::WrapRunnableNM(&NrUdpSocketIpc::release_use_s),
                 NS_DISPATCH_NORMAL);
 }
 
-void NrSocketIpc::release_use_s() {
+void NrUdpSocketIpc::release_use_s() {
   sThread->ReleaseUse();
 }
 #endif
 
-void NrSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
+void NrUdpSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
   ASSERT_ON_THREAD(sts_thread_);
 
   {
     ReentrantMonitorAutoEnter mon(monitor_);
     if (state_ != NR_CONNECTED) {
       return;
     }
   }
@@ -1429,16 +1476,458 @@ void NrSocketIpc::recv_callback_s(RefPtr
   //enqueue received message
   received_msgs_.push(msg);
 
   if ((poll_flags() & PR_POLL_READ)) {
     fire_callback(NR_ASYNC_WAIT_READ);
   }
 }
 
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+// TCPSocket.
+class NrTcpSocketIpc::TcpSocketReadyRunner: public nsRunnable
+{
+public:
+  explicit TcpSocketReadyRunner(NrTcpSocketIpc *sck)
+    : socket_(sck) {}
+
+  NS_IMETHODIMP Run() {
+    socket_->maybe_post_socket_ready();
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<NrTcpSocketIpc> socket_;
+};
+
+
+NS_IMPL_ISUPPORTS(NrTcpSocketIpc,
+                  nsITCPSocketCallback)
+
+NrTcpSocketIpc::NrTcpSocketIpc(nsIThread* aThread)
+  : NrSocketIpc(static_cast<nsIEventTarget*>(aThread)),
+    mirror_state_(NR_INIT),
+    state_(NR_INIT),
+    buffered_bytes_(0),
+    tracking_number_(0) {
+}
+
+NrTcpSocketIpc::~NrTcpSocketIpc()
+{
+  // also guarantees socket_child_ is released from the io_thread
+
+  // close(), but transfer the socket_child_ reference to die as well
+  RUN_ON_THREAD(io_thread_,
+                mozilla::WrapRunnableNM(&NrTcpSocketIpc::release_child_i,
+                                        socket_child_.forget().take(),
+                                        sts_thread_),
+                NS_DISPATCH_NORMAL);
+}
+
+//
+// nsITCPSocketCallback methods
+//
+NS_IMETHODIMP NrTcpSocketIpc::UpdateReadyState(uint32_t aReadyState) {
+  NrSocketIpcState temp = NR_INIT;
+  switch (static_cast<dom::TCPReadyState>(aReadyState)) {
+    case dom::TCPReadyState::Connecting:
+      temp = NR_CONNECTING;
+      break;
+    case dom::TCPReadyState::Open:
+      temp = NR_CONNECTED;
+      break;
+    case dom::TCPReadyState::Closing:
+      temp = NR_CLOSING;
+      break;
+    case dom::TCPReadyState::Closed:
+      temp = NR_CLOSED;
+      break;
+    default:
+      MOZ_ASSERT(false, "Invalid ReadyState");
+      return NS_OK;
+  }
+  if (mirror_state_ != temp) {
+    mirror_state_ = temp;
+    RUN_ON_THREAD(sts_thread_,
+                  mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                        &NrTcpSocketIpc::update_state_s,
+                                        temp),
+                  NS_DISPATCH_NORMAL);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::UpdateBufferedAmount(uint32_t buffered_amount,
+                                                   uint32_t tracking_number) {
+  RUN_ON_THREAD(sts_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                      &NrTcpSocketIpc::message_sent_s,
+                                      buffered_amount,
+                                      tracking_number),
+                NS_DISPATCH_NORMAL);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireDataArrayEvent(const nsAString& aType,
+                                                 const InfallibleTArray<uint8_t>& buffer) {
+  // Called when we received data.
+  uint8_t *buf = const_cast<uint8_t*>(buffer.Elements());
+
+  nsAutoPtr<DataBuffer> data_buf(new DataBuffer(buf, buffer.Length()));
+  nsRefPtr<nr_tcp_message> msg = new nr_tcp_message(data_buf);
+
+  RUN_ON_THREAD(sts_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                      &NrTcpSocketIpc::recv_message_s,
+                                      msg),
+                NS_DISPATCH_NORMAL);
+  return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireErrorEvent(const nsAString &type,
+                                             const nsAString &name) {
+  r_log(LOG_GENERIC, LOG_ERR,
+        "Error from TCPSocketChild: type: %s, name: %s",
+        NS_LossyConvertUTF16toASCII(type).get(), NS_LossyConvertUTF16toASCII(name).get());
+  socket_child_ = nullptr;
+
+  mirror_state_ = NR_CLOSED;
+  RUN_ON_THREAD(sts_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                      &NrTcpSocketIpc::update_state_s,
+                                      NR_CLOSED),
+                NS_DISPATCH_NORMAL);
+
+  return NS_OK;
+}
+
+// methods of nsITCPSocketCallback that we are not going to implement.
+
+NS_IMETHODIMP NrTcpSocketIpc::FireDataEvent(JSContext* aCx,
+                                            const nsAString &type,
+                                            const JS::HandleValue data) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireDataStringEvent(const nsAString &type,
+                                                  const nsACString &data) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireEvent(const nsAString &type) {
+  // XXX support type.mData == 'close' at least
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//
+// NrSocketBase methods.
+//
+int NrTcpSocketIpc::create(nr_transport_addr *addr) {
+  int r, _status;
+  nsresult rv;
+  int32_t port;
+  nsCString host;
+
+  if (state_ != NR_INIT) {
+    ABORT(R_INTERNAL);
+  }
+
+  sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    MOZ_ASSERT(false, "Failed to get STS thread");
+    ABORT(R_INTERNAL);
+  }
+
+  // Sanity check
+  if ((r=nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
+    ABORT(r);
+  }
+
+  if ((r=nr_transport_addr_copy(&my_addr_, addr))) {
+    ABORT(r);
+  }
+
+  _status = 0;
+abort:
+  return(_status);
+}
+
+int NrTcpSocketIpc::sendto(const void *msg, size_t len,
+                           int flags, nr_transport_addr *to) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::recvfrom(void * buf, size_t maxlen,
+                             size_t *len, int flags,
+                             nr_transport_addr *from) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::getaddr(nr_transport_addr *addrp) {
+  ASSERT_ON_THREAD(sts_thread_);
+  return nr_transport_addr_copy(addrp, &my_addr_);
+}
+
+void NrTcpSocketIpc::close() {
+  ASSERT_ON_THREAD(sts_thread_);
+
+  if (state_ == NR_CLOSED || state_ == NR_CLOSING) {
+    return;
+  }
+
+  state_ = NR_CLOSING;
+
+  RUN_ON_THREAD(io_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                      &NrTcpSocketIpc::close_i),
+                NS_DISPATCH_NORMAL);
+
+  //remove all enqueued messages
+  std::queue<RefPtr<nr_tcp_message>> empty;
+  std::swap(msg_queue_, empty);
+}
+
+int NrTcpSocketIpc::connect(nr_transport_addr *addr) {
+  nsCString remote_addr, local_addr;
+  int32_t remote_port, local_port;
+  int r, _status;
+  if ((r=nr_transport_addr_get_addrstring_and_port(addr,
+                                                   &remote_addr,
+                                                   &remote_port))) {
+    ABORT(r);
+  }
+
+  if ((r=nr_transport_addr_get_addrstring_and_port(&my_addr_,
+                                                   &local_addr,
+                                                   &local_port))) {
+    MOZ_ASSERT(false); // shouldn't fail as it was sanity-checked in ::create()
+    ABORT(r);
+  }
+
+  state_ = mirror_state_ = NR_CONNECTING;
+  RUN_ON_THREAD(io_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                             &NrTcpSocketIpc::connect_i,
+                             remote_addr,
+                             static_cast<uint16_t>(remote_port),
+                             local_addr,
+                             static_cast<uint16_t>(local_port)),
+                NS_DISPATCH_NORMAL);
+
+  // Make caller wait for ready to write.
+  _status = R_WOULDBLOCK;
+ abort:
+  return _status;
+}
+
+int NrTcpSocketIpc::write(const void *msg, size_t len, size_t *written) {
+  ASSERT_ON_THREAD(sts_thread_);
+  int _status = 0;
+  if (state_ != NR_CONNECTED) {
+    ABORT(R_FAILED);
+  }
+
+  if (buffered_bytes_ + len >= nsITCPSocketCallback::BUFFER_SIZE) {
+    ABORT(R_WOULDBLOCK);
+  }
+
+  buffered_bytes_ += len;
+  {
+    InfallibleTArray<uint8_t>* arr = new InfallibleTArray<uint8_t>();
+    arr->AppendElements(static_cast<const uint8_t*>(msg), len);
+    // keep track of un-acknowleged writes by tracking number.
+    writes_in_flight_.push_back(len);
+    RUN_ON_THREAD(io_thread_,
+                  mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                        &NrTcpSocketIpc::write_i,
+                                        nsAutoPtr<InfallibleTArray<uint8_t>>(arr),
+                                        ++tracking_number_),
+                  NS_DISPATCH_NORMAL);
+  }
+  *written = len;
+ abort:
+  return _status;
+}
+
+int NrTcpSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
+  int _status = 0;
+  if (state_ != NR_CONNECTED) {
+    ABORT(R_FAILED);
+  }
+
+  if (msg_queue_.size() == 0) {
+    ABORT(R_WOULDBLOCK);
+  }
+
+  {
+    nsRefPtr<nr_tcp_message> msg(msg_queue_.front());
+    size_t consumed_len = std::min(maxlen, msg->unread_bytes());
+    memcpy(buf, msg->reading_pointer(), consumed_len);
+    if (consumed_len < msg->unread_bytes()) {
+      // There is still something left in buffer.
+      msg->read_bytes += consumed_len;
+    } else {
+      msg_queue_.pop();
+    }
+    *len = consumed_len;
+  }
+
+ abort:
+  return _status;
+}
+
+int NrTcpSocketIpc::listen(int backlog) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+void NrTcpSocketIpc::connect_i(const nsACString &remote_addr,
+                               uint16_t remote_port,
+                               const nsACString &local_addr,
+                               uint16_t local_port) {
+  ASSERT_ON_THREAD(io_thread_);
+  mirror_state_ = NR_CONNECTING;
+
+  dom::TCPSocketChild* child = new dom::TCPSocketChild(NS_ConvertUTF8toUTF16(remote_addr), remote_port);
+  socket_child_ = child;
+
+  // XXX remove remote!
+  socket_child_->SendWindowlessOpenBind(this,
+                                        remote_addr, remote_port,
+                                        local_addr, local_port,
+                                        /* use ssl */ false);
+}
+
+void NrTcpSocketIpc::write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr,
+                             uint32_t tracking_number) {
+  ASSERT_ON_THREAD(io_thread_);
+  if (!socket_child_) {
+    return;
+  }
+  socket_child_->SendSendArray(*arr, tracking_number);
+}
+
+void NrTcpSocketIpc::close_i() {
+  ASSERT_ON_THREAD(io_thread_);
+  mirror_state_ = NR_CLOSING;
+  if (!socket_child_) {
+    return;
+  }
+  socket_child_->SendClose();
+}
+
+// close(), but transfer the socket_child_ reference to die as well
+// static
+void NrTcpSocketIpc::release_child_i(dom::TCPSocketChild* aChild,
+                                     nsCOMPtr<nsIEventTarget> sts_thread) {
+  nsRefPtr<dom::TCPSocketChild> socket_child_ref =
+    already_AddRefed<dom::TCPSocketChild>(aChild);
+  if (socket_child_ref) {
+    socket_child_ref->SendClose();
+  }
+  // io_thread_ is MainThread, so no use to release
+}
+
+void NrTcpSocketIpc::message_sent_s(uint32_t buffered_amount,
+                                    uint32_t tracking_number) {
+  ASSERT_ON_THREAD(sts_thread_);
+
+  size_t num_unacked_writes = tracking_number_ - tracking_number;
+  while (writes_in_flight_.size() > num_unacked_writes) {
+    writes_in_flight_.pop_front();
+  }
+
+  for (size_t unacked_write_len : writes_in_flight_) {
+    buffered_amount += unacked_write_len;
+  }
+
+  r_log(LOG_GENERIC, LOG_ERR,
+        "UpdateBufferedAmount: (tracking %u): %u, waiting: %s",
+        tracking_number, buffered_amount,
+        (poll_flags() & PR_POLL_WRITE) ? "yes" : "no");
+
+  buffered_bytes_ = buffered_amount;
+  maybe_post_socket_ready();
+}
+
+void NrTcpSocketIpc::recv_message_s(nr_tcp_message *msg) {
+  ASSERT_ON_THREAD(sts_thread_);
+  msg_queue_.push(msg);
+  maybe_post_socket_ready();
+}
+
+void NrTcpSocketIpc::update_state_s(NrSocketIpcState next_state) {
+  ASSERT_ON_THREAD(sts_thread_);
+  // only allow valid transitions
+  switch (state_) {
+    case NR_CONNECTING:
+      if (next_state == NR_CONNECTED) {
+        state_ = NR_CONNECTED;
+        maybe_post_socket_ready();
+      } else {
+        state_ = next_state; // all states are valid from CONNECTING
+      }
+      break;
+    case NR_CONNECTED:
+      if (next_state != NR_CONNECTING) {
+        state_ = next_state;
+      }
+      break;
+    case NR_CLOSING:
+      if (next_state == NR_CLOSED) {
+        state_ = next_state;
+      }
+      break;
+    case NR_CLOSED:
+      break;
+    default:
+      MOZ_CRASH("update_state_s while in illegal state");
+  }
+}
+
+void NrTcpSocketIpc::maybe_post_socket_ready() {
+  bool has_event = false;
+  if (state_ == NR_CONNECTED) {
+    if (poll_flags() & PR_POLL_WRITE) {
+      // This effectively polls via the event loop until the
+      // NR_ASYNC_WAIT_WRITE is no longer armed.
+      if (buffered_bytes_ < nsITCPSocketCallback::BUFFER_SIZE) {
+        r_log(LOG_GENERIC, LOG_INFO, "Firing write callback (%u)",
+              (uint32_t)buffered_bytes_);
+        fire_callback(NR_ASYNC_WAIT_WRITE);
+        has_event = true;
+      }
+    }
+    if (poll_flags() & PR_POLL_READ) {
+      if (msg_queue_.size()) {
+        r_log(LOG_GENERIC, LOG_INFO, "Firing read callback (%u)",
+              (uint32_t)msg_queue_.size());
+        fire_callback(NR_ASYNC_WAIT_READ);
+        has_event = true;
+      }
+    }
+  }
+
+  // If any event has been posted, we post a runnable to see
+  // if the events have to be posted again.
+  if (has_event) {
+    nsRefPtr<TcpSocketReadyRunner> runnable = new TcpSocketReadyRunner(this);
+    NS_DispatchToCurrentThread(runnable);
+  }
+}
+#endif
+
 }  // close namespace
 
 
 using namespace mozilla;
 
 // Bridge to the nr_socket interface
 static int nr_socket_local_destroy(void **objp);
 static int nr_socket_local_sendto(void *obj,const void *msg, size_t len,
@@ -1469,26 +1958,40 @@ static nr_socket_vtbl nr_socket_local_vt
   nr_socket_local_read,
   nr_socket_local_close,
   nr_socket_local_listen,
   nr_socket_local_accept
 };
 
 int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp) {
   RefPtr<NrSocketBase> sock;
+  int r, _status;
 
   // create IPC bridge for content process
   if (XRE_IsParentProcess()) {
     sock = new NrSocket();
   } else {
-    sock = new NrSocketIpc();
+    switch (addr->protocol) {
+      case IPPROTO_UDP:
+        sock = new NrUdpSocketIpc();
+        break;
+      case IPPROTO_TCP:
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+        {
+          nsCOMPtr<nsIThread> main_thread;
+          NS_GetMainThread(getter_AddRefs(main_thread));
+          sock = new NrTcpSocketIpc(main_thread.get());
+        }
+#else
+        ABORT(R_REJECTED);
+#endif
+        break;
+    }
   }
 
-  int r, _status;
-
   r = sock->create(addr);
   if (r)
     ABORT(r);
 
   r = nr_socket_create_int(static_cast<void *>(sock),
                            sock->vtbl(), sockp);
   if (r)
     ABORT(r);
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -56,27 +56,36 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #include "nsASocketHandler.h"
 #include "nsISocketTransportService.h"
 #include "nsXPCOM.h"
 #include "nsIEventTarget.h"
 #include "nsIUDPSocketChild.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
+#include "nsITCPSocketCallback.h"
 #include "databuffer.h"
 #include "m_cpp_utils.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
 
 // Stub declaration for nICEr type
 typedef struct nr_socket_vtbl_ nr_socket_vtbl;
 typedef struct nr_socket_ nr_socket;
 
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+namespace mozilla {
+namespace dom {
+class TCPSocketChild;
+}
+}
+#endif
+
 namespace mozilla {
 
 namespace net {
   union NetAddr;
 }
 
 class NrSocketBase {
 public:
@@ -204,29 +213,44 @@ public:
   enum NrSocketIpcState {
     NR_INIT,
     NR_CONNECTING,
     NR_CONNECTED,
     NR_CLOSING,
     NR_CLOSED,
   };
 
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrSocketIpc, override)
+  NrSocketIpc(nsIEventTarget* aThread);
+
+protected:
+  nsCOMPtr<nsIEventTarget> sts_thread_;
+  // Note: for UDP PBackground, this is a thread held by SingletonThreadHolder.
+  // For TCP PNecko, this is MainThread (and TCPSocket requires MainThread currently)
+  const nsCOMPtr<nsIEventTarget> io_thread_;
+  virtual ~NrSocketIpc() {};
+
+private:
+  DISALLOW_COPY_ASSIGN(NrSocketIpc);
+};
+
+class NrUdpSocketIpc : public NrSocketIpc {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrUdpSocketIpc, override)
 
   NS_IMETHODIMP CallListenerError(const nsACString &message,
                                   const nsACString &filename,
                                   uint32_t line_number);
   NS_IMETHODIMP CallListenerReceivedData(const nsACString &host,
                                          uint16_t port,
                                          const uint8_t *data,
                                          uint32_t data_length);
   NS_IMETHODIMP CallListenerOpened();
   NS_IMETHODIMP CallListenerClosed();
 
-  NrSocketIpc();
+  NrUdpSocketIpc();
 
   // Implementations of the NrSocketBase APIs
   virtual int create(nr_transport_addr *addr) override;
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to) override;
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from) override;
@@ -234,58 +258,142 @@ public:
   virtual void close() override;
   virtual int connect(nr_transport_addr *addr) override;
   virtual int write(const void *msg, size_t len, size_t *written) override;
   virtual int read(void* buf, size_t maxlen, size_t *len) override;
   virtual int listen(int backlog) override;
   virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
 
 private:
-  virtual ~NrSocketIpc();
+  virtual ~NrUdpSocketIpc();
 
-  DISALLOW_COPY_ASSIGN(NrSocketIpc);
-
-  static nsIThread* GetIOThreadAndAddUse_s();
+  DISALLOW_COPY_ASSIGN(NrUdpSocketIpc);
 
   // Main or private thread executors of the NrSocketBase APIs
   void create_i(const nsACString &host, const uint16_t port);
   void sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf);
   void close_i();
 #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
   static void release_child_i(nsIUDPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
   static void release_use_s();
 #endif
   // STS thread executor
   void recv_callback_s(RefPtr<nr_udp_message> msg);
 
+  ReentrantMonitor monitor_; // protects err_and state_
   bool err_;
   NrSocketIpcState state_;
-  std::queue<RefPtr<nr_udp_message> > received_msgs_;
+
+  std::queue<RefPtr<nr_udp_message>> received_msgs_;
 
   nsRefPtr<nsIUDPSocketChild> socket_child_; // only accessed from the io_thread
-  nsCOMPtr<nsIEventTarget> sts_thread_;
-  const nsCOMPtr<nsIEventTarget> io_thread_;
-  ReentrantMonitor monitor_;
 };
 
 // The socket child holds onto one of these, which just passes callbacks
 // through and makes sure the ref to the NrSocketIpc is released on STS.
-class NrSocketIpcProxy : public nsIUDPSocketInternal {
+class NrUdpSocketIpcProxy : public nsIUDPSocketInternal {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIUDPSOCKETINTERNAL
 
-  nsresult Init(const nsRefPtr<NrSocketIpc>& socket);
+  nsresult Init(const nsRefPtr<NrUdpSocketIpc>& socket);
+
+private:
+  virtual ~NrUdpSocketIpcProxy();
+
+  nsRefPtr<NrUdpSocketIpc> socket_;
+  nsCOMPtr<nsIEventTarget> sts_thread_;
+};
+
+struct nr_tcp_message {
+  explicit nr_tcp_message(nsAutoPtr<DataBuffer> &data)
+    : read_bytes(0)
+    , data(data) {
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_tcp_message);
+
+  const uint8_t *reading_pointer() const {
+    return data->data() + read_bytes;
+  }
+
+  size_t unread_bytes() const {
+    return data->len() - read_bytes;
+  }
+
+  size_t read_bytes;
 
 private:
-  virtual ~NrSocketIpcProxy();
+  ~nr_tcp_message() {}
+  DISALLOW_COPY_ASSIGN(nr_tcp_message);
+
+  nsAutoPtr<DataBuffer> data;
+};
+
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+class NrTcpSocketIpc : public NrSocketIpc,
+                       public nsITCPSocketCallback {
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSITCPSOCKETCALLBACK
+
+  explicit NrTcpSocketIpc(nsIThread* aThread);
+
+  // Implementations of the NrSocketBase APIs
+  virtual int create(nr_transport_addr *addr) override;
+  virtual int sendto(const void *msg, size_t len,
+                     int flags, nr_transport_addr *to) override;
+  virtual int recvfrom(void * buf, size_t maxlen,
+                       size_t *len, int flags,
+                       nr_transport_addr *from) override;
+  virtual int getaddr(nr_transport_addr *addrp) override;
+  virtual void close() override;
+  virtual int connect(nr_transport_addr *addr) override;
+  virtual int write(const void *msg, size_t len, size_t *written) override;
+  virtual int read(void* buf, size_t maxlen, size_t *len) override;
+  virtual int listen(int backlog) override;
+  virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
 
-  nsRefPtr<NrSocketIpc> socket_;
-  nsCOMPtr<nsIEventTarget> sts_thread_;
+private:
+  class TcpSocketReadyRunner;
+  DISALLOW_COPY_ASSIGN(NrTcpSocketIpc);
+  virtual ~NrTcpSocketIpc();
+
+  // Main thread executors of the NrSocketBase APIs
+  void connect_i(const nsACString &remote_addr,
+                 uint16_t remote_port,
+                 const nsACString &local_addr,
+                 uint16_t local_port);
+  void write_i(nsAutoPtr<InfallibleTArray<uint8_t>> buf,
+               uint32_t tracking_number);
+  void close_i();
+
+  static void release_child_i(dom::TCPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
+
+  // STS thread executor
+  void message_sent_s(uint32_t bufferedAmount, uint32_t tracking_number);
+  void recv_message_s(nr_tcp_message *msg);
+  void update_state_s(NrSocketIpcState next_state);
+  void maybe_post_socket_ready();
+
+  // Accessed from UpdateReadyState (not sts_thread) to avoid sending
+  // runnables when not needed
+  NrSocketIpcState mirror_state_;
+
+  // variables that can only be accessed on STS.
+  NrSocketIpcState state_;
+  std::queue<RefPtr<nr_tcp_message>> msg_queue_;
+  uint32_t buffered_bytes_;
+  uint32_t tracking_number_;
+  std::deque<size_t> writes_in_flight_;
+
+  // main thread.
+  nsRefPtr<dom::TCPSocketChild> socket_child_;
 };
+#endif
 
 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
                                  nr_transport_addr *addr,
                                  int protocol);
 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
                                 nr_transport_addr *addr,
                                 int protocol, int keep);
 int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
--- a/media/mtransport/standalone/moz.build
+++ b/media/mtransport/standalone/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 Library('mtransport_standalone')
 
 include('../common.build')
+include("/ipc/chromium/chromium-config.mozbuild")
 
 # These files cannot be built in unified mode because of the redefinition of
 # getLogModule, UNIMPLEMENTED, nr_socket_long_term_violation_time,
 # nr_socket_short_term_violation_time.
 SOURCES += mtransport_cppsrcs
 
 FORCE_STATIC_LIB = True
 
--- a/media/mtransport/testlib/moz.build
+++ b/media/mtransport/testlib/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include('../common.build')
+include("/ipc/chromium/chromium-config.mozbuild")
 
 # These files cannot be built in unified mode because of the redefinition of
 # getLogModule, UNIMPLEMENTED, nr_socket_long_term_violation_time,
 # nr_socket_short_term_violation_time.
 SOURCES += mtransport_cppsrcs
 
 Library('mtransport_s')
 
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
@@ -33,16 +33,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 #include <nr_api.h>
 
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <assert.h>
+#include <inttypes.h>
 
 #include "p_buf.h"
 #include "nr_socket.h"
 #include "stun.h"
 #include "nr_socket_buffered_stun.h"
 
 #define NR_MAX_FRAME_SIZE 0xFFFF
 
@@ -369,18 +370,20 @@ static int nr_socket_buffered_stun_close
 
 static void nr_socket_buffered_stun_connected_cb(NR_SOCKET s, int how, void *arg)
 {
   nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg;
 
   assert(!sock->connected);
 
   sock->connected = 1;
-  if (sock->pending)
+  if (sock->pending) {
+    r_log(LOG_GENERIC, LOG_INFO, "Invoking writable_cb on connected (%u)", (uint32_t) sock->pending);
     nr_socket_buffered_stun_writable_cb(s, how, arg);
+  }
 }
 
 static int nr_socket_buffered_stun_connect(void *obj, nr_transport_addr *addr)
 {
   nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
   int r, _status;
 
   if ((r=nr_transport_addr_copy(&sock->remote_addr, addr)))
@@ -393,16 +396,17 @@ static int nr_socket_buffered_stun_conne
       if ((r=nr_socket_getfd(sock->inner, &fd)))
         ABORT(r);
 
       NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_connected_cb, sock);
       ABORT(R_WOULDBLOCK);
     }
     ABORT(r);
   } else {
+    r_log(LOG_GENERIC, LOG_INFO, "Connected without blocking");
     sock->connected = 1;
   }
 
   _status=0;
 abort:
   return(_status);
 }
 
@@ -427,48 +431,62 @@ static int nr_socket_buffered_stun_write
   int already_armed = 0;
   int r,_status;
   size_t written2 = 0;
   size_t original_len = len;
 
   /* Buffers are close to full, report error. Do this now so we never
      get partial writes */
   if ((sock->pending + len) > sock->max_pending) {
-    r_log(LOG_GENERIC, LOG_INFO, "Write buffer for %s full", sock->remote_addr.as_string);
+    r_log(LOG_GENERIC, LOG_INFO, "Write buffer for %s full (%u + %u > %u) - re-arming @%p",
+          sock->remote_addr.as_string, (uint32_t)sock->pending, (uint32_t)len, (uint32_t)sock->max_pending,
+          &(sock->pending));
     ABORT(R_WOULDBLOCK);
   }
 
 
   if (sock->connected && !sock->pending) {
     r = nr_socket_write(sock->inner, msg, len, &written2, 0);
     if (r) {
-      if (r != R_WOULDBLOCK)
+      if (r != R_WOULDBLOCK) {
+        r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d",
+              sock->remote_addr.as_string, r);
         ABORT(r);
+      }
+      r_log(LOG_GENERIC, LOG_INFO, "Write of %" PRIu64 " blocked for %s",
+            (uint64_t) len, sock->remote_addr.as_string);
 
       written2=0;
     }
   } else {
     already_armed = 1;
   }
 
   /* Buffer what's left */
   len -= written2;
 
   if (len) {
     if ((r=nr_p_buf_write_to_chain(sock->p_bufs, &sock->pending_writes,
-                                     ((UCHAR *)msg) + written2, len)))
+                                   ((UCHAR *)msg) + written2, len))) {
+      r_log(LOG_GENERIC, LOG_ERR, "Write_to_chain error for %s - %d",
+            sock->remote_addr.as_string, r);
+
       ABORT(r);
+    }
 
     sock->pending += len;
   }
 
   if (sock->pending && !already_armed) {
       if ((r=nr_socket_buffered_stun_arm_writable_cb(sock)))
         ABORT(r);
   }
+  r_log(LOG_GENERIC, LOG_INFO, "Write buffer not empty for %s  %u - %s armed (@%p)",
+        sock->remote_addr.as_string, (uint32_t)sock->pending,
+        already_armed ? "already" : "", &sock->pending);
 
   *written = original_len;
 
   _status=0;
 abort:
   return _status;
 }
 
@@ -481,34 +499,42 @@ static void nr_socket_buffered_stun_writ
   /* Try to flush */
   STAILQ_FOREACH_SAFE(n1, &sock->pending_writes, entry, n2) {
     size_t written = 0;
 
     if ((r=nr_socket_write(sock->inner, n1->data + n1->r_offset,
                            n1->length - n1->r_offset,
                            &written, 0))) {
 
+      r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d",
+            sock->remote_addr.as_string, r);
       ABORT(r);
     }
 
     n1->r_offset += written;
     assert(sock->pending >= written);
     sock->pending -= written;
 
     if (n1->r_offset < n1->length) {
       /* We wrote something, but not everything */
+      r_log(LOG_GENERIC, LOG_INFO, "Write in callback didn't write all (remaining %u of %u) for %s",
+            n1->length - n1->r_offset, n1->length,
+            sock->remote_addr.as_string);
       ABORT(R_WOULDBLOCK);
     }
 
     /* We are done with this p_buf */
     STAILQ_REMOVE_HEAD(&sock->pending_writes, entry);
     nr_p_buf_free(sock->p_bufs, n1);
   }
 
   assert(!sock->pending);
   _status=0;
 abort:
+  r_log(LOG_GENERIC, LOG_INFO, "Writable_cb %s (%u (%p) pending)",
+        sock->remote_addr.as_string, (uint32_t)sock->pending, &(sock->pending));
   if (_status && _status != R_WOULDBLOCK) {
+    r_log(LOG_GENERIC, LOG_ERR, "Failure in writable_cb: %d", _status);
     nr_socket_buffered_stun_failed(sock);
   } else if (sock->pending) {
     nr_socket_buffered_stun_arm_writable_cb(sock);
   }
 }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -617,22 +617,16 @@ PeerConnectionConfiguration::AddIceServe
     }
     if (port == -1)
       port = (isStuns || isTurns)? 5349 : 3478;
 
     if (isTurn || isTurns) {
       NS_ConvertUTF16toUTF8 credential(aServer.mCredential);
       NS_ConvertUTF16toUTF8 username(aServer.mUsername);
 
-      // Bug 1039655 - TURN TCP is not e10s ready
-      if ((transport == kNrIceTransportTcp) &&
-          (!XRE_IsParentProcess())) {
-        continue;
-      }
-
       if (!addTurnServer(host.get(), port,
                          username.get(),
                          credential.get(),
                          (transport.IsEmpty() ?
                           kNrIceTransportUdp : transport.get()))) {
         return NS_ERROR_FAILURE;
       }
     } else {