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 296092 8eda15c59d265f755582342dd027e97959b2429b
parent 296091 dfaa6b689bc00d216add359d8ca8a8e40c65a109
child 296093 41cce993981073d8319cab239570192c3aacd3d1
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbwc, jdm
bugs950660
milestone43.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 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 {