Bug 770778 - Make TCPSocket e10s-friendly. r=bent
authorJosh Matthews <josh@joshmatthews.net>
Mon, 24 Sep 2012 14:53:49 -0400
changeset 108455 806fa3680a9138de39b0441ef2bb139069c08772
parent 108454 60d3d0e84b2b2b454b1a80fce7cbf1fec40f2560
child 108456 519f41fa596f7e3ef09ea28f28ffcad390e45c01
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersbent
bugs770778
milestone18.0a1
Bug 770778 - Make TCPSocket e10s-friendly. r=bent
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
dom/base/Makefile.in
dom/network/interfaces/Makefile.in
dom/network/interfaces/nsIDOMTCPSocket.idl
dom/network/interfaces/nsITCPSocketChild.idl
dom/network/interfaces/nsITCPSocketParent.idl
dom/network/src/Makefile.in
dom/network/src/PTCPSocket.ipdl
dom/network/src/TCPSocket.js
dom/network/src/TCPSocket.manifest
dom/network/src/TCPSocketChild.cpp
dom/network/src/TCPSocketChild.h
dom/network/src/TCPSocketParent.cpp
dom/network/src/TCPSocketParent.h
dom/network/src/TCPSocketParentIntermediary.js
dom/network/src/ipdl.mk
dom/network/tests/Makefile.in
dom/network/tests/unit/test_tcpsocket.js
dom/network/tests/unit_ipc/test_tcpsocket_ipc.js
dom/network/tests/unit_ipc/xpcshell.ini
ipc/ipdl/Makefile.in
layout/build/nsLayoutModule.cpp
mobile/android/installer/package-manifest.in
mobile/xul/installer/package-manifest.in
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
testing/xpcshell/xpcshell.ini
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -489,16 +489,17 @@
 
 @BINPATH@/components/Activities.manifest
 @BINPATH@/components/ActivityOptions.js
 @BINPATH@/components/ActivityProxy.js
 @BINPATH@/components/ActivityRequestHandler.js
 @BINPATH@/components/ActivityWrapper.js
 
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 @BINPATH@/components/AppProtocolHandler.js
 @BINPATH@/components/AppProtocolHandler.manifest
 
 @BINPATH@/components/Payment.js
 @BINPATH@/components/PaymentFlowInfo.js
 @BINPATH@/components/PaymentRequestInfo.js
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -482,16 +482,17 @@
 
 @BINPATH@/components/PermissionSettings.js
 @BINPATH@/components/PermissionSettings.manifest
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
 @BINPATH@/components/AlarmsManager.js
 @BINPATH@/components/AlarmsManager.manifest
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 #ifdef ENABLE_MARIONETTE
 @BINPATH@/chrome/marionette@JAREXT@
 @BINPATH@/chrome/marionette.manifest
 @BINPATH@/components/MarionetteComponents.manifest
 @BINPATH@/components/marionettecomponent.js
 #endif
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -55,16 +55,17 @@ EXPORTS = \
   nsIScriptContext.h	\
   nsIScriptExternalNameSet.h \
   nsIScriptGlobalObject.h \
   nsIScriptGlobalObjectOwner.h \
   nsIScriptNameSpaceManager.h \
   nsIScriptObjectPrincipal.h \
   nsIScriptRuntime.h \
   nsIScriptTimeoutHandler.h \
+  nsJSUtils.h \
   nsPIDOMWindow.h \
   nsPIWindowRoot.h \
   nsFocusManager.h \
   nsWrapperCache.h \
   nsWrapperCacheInlines.h \
   nsContentPermissionHelper.h \
   nsStructuredCloneContainer.h \
   nsWindowMemoryReporter.h \
--- a/dom/network/interfaces/Makefile.in
+++ b/dom/network/interfaces/Makefile.in
@@ -13,16 +13,18 @@ XPIDL_MODULE = dom_network
 
 include $(topsrcdir)/dom/dom-config.mk
 
 XPIDLSRCS = \
   nsIDOMNavigatorNetwork.idl \
   nsIDOMConnection.idl \
   nsIDOMUSSDReceivedEvent.idl \
   nsIDOMTCPSocket.idl \
+  nsITCPSocketParent.idl \
+  nsITCPSocketChild.idl \
   nsIDOMDataErrorEvent.idl \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 XPIDLSRCS += \
   nsIDOMMobileConnection.idl \
   nsIMobileConnectionProvider.idl \
   nsINavigatorMobileConnection.idl \
--- a/dom/network/interfaces/nsIDOMTCPSocket.idl
+++ b/dom/network/interfaces/nsIDOMTCPSocket.idl
@@ -169,16 +169,40 @@ interface nsIDOMTCPSocket : nsISupports
    * close.
    *
    * If onerror was not called before onclose, then either side cleanly
    * closed the connection.
    */
   attribute jsval onclose;
 };
 
+/*
+ * Internal interfaces for use in cross-process socket implementation.
+ * Needed to account for multiple possible types that can be provided to
+ * the socket callbacks as arguments.
+ */
+[scriptable, uuid(fff9ed4a-5e94-4fc0-84e4-7f4d35d0a26c)]
+interface nsITCPSocketInternal : nsISupports {
+  // Trigger the callback for |type| and provide an Error() object with the given data
+  void callListenerError(in DOMString type, in DOMString message, in DOMString filename,
+                         in uint32_t lineNumber, in uint32_t columnNumber);
+
+  // Trigger the callback for |type| and provide a string argument
+  void callListenerData(in DOMString type, in DOMString data);
+
+  // Trigger the callback for |type| and provide a Uint8Array argument
+  void callListenerArrayBuffer(in DOMString type, in jsval data);
+
+  // Trigger the callback for |type| with no argument
+  void callListenerVoid(in DOMString type);
+
+  // Update the DOM object's readyState and bufferedAmount values with the provided data
+  void updateReadyStateAndBuffered(in DOMString readyState, in uint32_t bufferedAmount);
+};
+
 /**
  * nsITCPSocketEvent is the event object which is passed as the
  * first argument to all the event handler callbacks. It contains
  * the socket that was associated with the event, the type of event,
  * and the data associated with the event (if any).
  */
 
 [scriptable, uuid(0f2abcca-b483-4539-a3e8-345707f75c44)]
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsITCPSocketChild.idl
@@ -0,0 +1,25 @@
+/* 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 "domstubs.idl"
+
+interface nsITCPSocketInternal;
+interface nsIDOMWindow;
+
+// Interface to allow the content process socket to reach the IPC bridge.
+[scriptable, uuid(a589d96f-7e09-4edf-a01a-eb4951f42f37)]
+interface nsITCPSocketChild : nsISupports
+{
+  // Tell the chrome process to open a corresponding connection with the given parameters
+  [implicit_jscontext]
+  void open(in nsITCPSocketInternal socket, in DOMString host,
+            in unsigned short port, in boolean ssl, in DOMString binaryType,
+            in nsIDOMWindow window, in jsval socketVal);
+
+  // Tell the chrome process to perform equivalent operations to all following methods
+  [implicit_jscontext] void send(in jsval data);
+  void resume();
+  void suspend();
+  void close();
+};
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsITCPSocketParent.idl
@@ -0,0 +1,39 @@
+/* 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 "domstubs.idl"
+
+interface nsIDOMTCPSocket;
+
+// Interface required to allow the TCP socket object in the parent process
+// to talk to the parent IPC actor
+[scriptable, uuid(4e7246c6-a8b3-426d-9c17-76dab1e1e14a)]
+interface nsITCPSocketParent : nsISupports
+{
+  [implicit_jscontext] void initJS(in jsval intermediary);
+
+  // Trigger a callback in the content process for |type|, providing a serialized
+  // argument of |data|, and update the child's readyState and bufferedAmount values
+  // with the given values.
+  [implicit_jscontext] void sendCallback(in DOMString type,
+                                         in jsval data,
+                                         in DOMString readyState,
+                                         in uint32_t bufferedAmount);
+};
+
+// Intermediate class to handle sending multiple possible data types
+// and kicking off the chrome process socket object's connection.
+[scriptable, uuid(afa42841-a6cb-4a91-912f-93099f6a3d18)]
+interface nsITCPSocketIntermediary : nsISupports {
+  // Open the connection to the server with the given parameters
+  nsIDOMTCPSocket open(in nsITCPSocketParent parent,
+                       in DOMString host, in unsigned short port,
+                       in boolean useSSL, in DOMString binaryType);
+
+  // Send a basic string along the connection
+  void sendString(in DOMString data);
+
+  // Send a typed array
+  void sendArrayBuffer(in jsval data);
+};
--- a/dom/network/src/Makefile.in
+++ b/dom/network/src/Makefile.in
@@ -11,34 +11,39 @@ include $(DEPTH)/config/autoconf.mk
 
 LIBRARY_NAME     = dom_network_s
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 FAIL_ON_WARNINGS := 1
 
 EXTRA_COMPONENTS = \
 	TCPSocket.js \
+	TCPSocketParentIntermediary.js \
 	TCPSocket.manifest \
 	$(NULL)
 
 include $(topsrcdir)/dom/dom-config.mk
 
 EXPORTS_NAMESPACES = mozilla/dom/network
 
 EXPORTS_mozilla/dom/network = \
   Utils.h \
   Types.h \
   Constants.h \
+  TCPSocketChild.h \
+  TCPSocketParent.h \
   $(NULL)
 
 CPPSRCS = \
   Connection.cpp \
   Utils.cpp \
   USSDReceivedEvent.cpp \
   DataErrorEvent.cpp \
+  TCPSocketParent.cpp \
+  TCPSocketChild.cpp \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 CPPSRCS += \
   MobileConnection.cpp \
   $(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/dom/network/src/PTCPSocket.ipdl
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PNecko;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using mozilla::void_t;
+
+struct JSError {
+  nsString message;
+  nsString filename;
+  uint32_t lineNumber;
+  uint32_t columnNumber;
+};
+
+union SendableData {
+  uint8_t[];
+  nsString;
+};
+
+union CallbackData {
+  void_t;
+  SendableData;
+  JSError;
+};
+
+namespace mozilla {
+namespace net {
+
+//-------------------------------------------------------------------
+protocol PTCPSocket
+{
+  manager PNecko;
+
+parent:
+  Data(SendableData data);
+  Suspend();
+  Resume();
+  Close();
+  RequestDelete();
+
+child:
+  Callback(nsString type, CallbackData data,
+           nsString readyState, uint32_t bufferedAmount);
+  __delete__();
+};
+
+
+} // namespace net
+} // namespace mozilla
+
--- a/dom/network/src/TCPSocket.js
+++ b/dom/network/src/TCPSocket.js
@@ -137,16 +137,22 @@ TCPSocket.prototype = {
   // Output stream machinery
   _multiplexStream: null,
   _multiplexStreamCopier: null,
 
   _asyncCopierActive: false,
   _waitingForDrain: false,
   _suspendCount: 0,
 
+  // Reported parent process buffer
+  _bufferedAmount: 0,
+
+  // IPC socket actor
+  _socketBridge: null,
+
   // Public accessors.
   get readyState() {
     return this._readyState;
   },
   get binaryType() {
     return this._binaryType;
   },
   get host() {
@@ -154,16 +160,19 @@ TCPSocket.prototype = {
   },
   get port() {
     return this._port;
   },
   get ssl() {
     return this._ssl;
   },
   get bufferedAmount() {
+    if (this._inChild) {
+      return this._bufferedAmount;
+    }
     return this._multiplexStream.available();
   },
   get onopen() {
     return this._onopen;
   },
   set onopen(f) {
     this._onopen = f;
   },
@@ -248,18 +257,46 @@ TCPSocket.prototype = {
 
   callListener: function ts_callListener(type, data) {
     if (!this[type])
       return;
 
     this[type].call(null, new TCPSocketEvent(type, this, data || ""));
   },
 
+  /* nsITCPSocketInternal methods */
+  callListenerError: function ts_callListenerError(type, message, filename,
+                                                   lineNumber, columnNumber) {
+    this.callListener(type, new Error(message, filename, lineNumber, columnNumber));
+  },
+
+  callListenerData: function ts_callListenerString(type, data) {
+    this.callListener(type, data);
+  },
+
+  callListenerArrayBuffer: function ts_callListenerArrayBuffer(type, data) {
+    this.callListener(type, data);
+  },
+
+  callListenerVoid: function ts_callListenerVoid(type) {
+    this.callListener(type);
+  },
+
+  updateReadyStateAndBuffered: function ts_setReadyState(readyState, bufferedAmount) {
+    this._readyState = readyState;
+    this._bufferedAmount = bufferedAmount;
+  },
+  /* end nsITCPSocketInternal methods */
+
+  initWindowless: function ts_initWindowless() {
+    return Services.prefs.getBoolPref("dom.mozTCPSocket.enabled");
+  },
+
   init: function ts_init(aWindow) {
-    if (!Services.prefs.getBoolPref("dom.mozTCPSocket.enabled"))
+    if (!this.initWindowless())
       return null;
 
     let principal = aWindow.document.nodePrincipal;
     let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
                    .getService(Ci.nsIScriptSecurityManager);
 
     let perm = principal == secMan.getSystemPrincipal()
                  ? Ci.nsIPermissionManager.ALLOW_ACTION
@@ -296,25 +333,33 @@ TCPSocket.prototype = {
         // Clean up our socket
         this.close();
       }
     }
   },
 
   // nsIDOMTCPSocket
   open: function ts_open(host, port, options) {
+    if (!this.initWindowless())
+      return null;
+
+    this._inChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
+                       .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+    LOG("content process: " + (this._inChild ? "true" : "false") + "\n");
+
     // in the testing case, init won't be called and
     // hasPrivileges will be null. We want to proceed to test.
     if (this._hasPrivileges !== true && this._hasPrivileges !== null) {
       throw new Error("TCPSocket does not have permission in this context.\n");
     }
     let that = new TCPSocket();
 
     that.useWin = this.useWin;
     that.innerWindowID = this.innerWindowID;
+    that._inChild = this._inChild;
 
     LOG("window init: " + that.innerWindowID);
     Services.obs.addObserver(that, "inner-window-destroyed", true);
 
     LOG("startup called\n");
     LOG("Host info: " + host + ":" + port + "\n");
 
     that._readyState = kCONNECTING;
@@ -326,16 +371,24 @@ TCPSocket.prototype = {
       } else {
           that._ssl = false;
       }
       that._binaryType = options.binaryType || that._binaryType;
     }
 
     LOG("SSL: " + that.ssl + "\n");
 
+    if (this._inChild) {
+      that._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"]
+                             .createInstance(Ci.nsITCPSocketChild);
+      that._socketBridge.open(that, host, port, !!that._ssl,
+                              that._binaryType, this.useWin, this);
+      return that;
+    }
+
     let transport = that._transport = this._createTransport(host, port, that._ssl);
     transport.setEventSink(that, Services.tm.currentThread);
     transport.securityCallbacks = new SecurityCallbacks(that);
 
     that._socketInputStream = transport.openInputStream(0, 0, 0);
     that._socketOutputStream = transport.openOutputStream(
       Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
 
@@ -367,27 +420,36 @@ TCPSocket.prototype = {
 
   close: function ts_close() {
     if (this._readyState === kCLOSED || this._readyState === kCLOSING)
       return;
 
     LOG("close called\n");
     this._readyState = kCLOSING;
 
+    if (this._inChild) {
+      this._socketBridge.close();
+      return;
+    }
+
     if (!this._multiplexStream.count) {
       this._socketOutputStream.close();
     }
     this._socketInputStream.close();
   },
 
   send: function ts_send(data) {
     if (this._readyState !== kOPEN) {
       throw new Error("Socket not open.");
     }
 
+    if (this._inChild) {
+      this._socketBridge.send(data);
+    }
+
     let new_stream = new StringInputStream();
     if (this._binaryType === "arraybuffer") {
       // It would be really nice if there were an interface
       // that took an ArrayBuffer like StringInputStream takes
       // a string. There is one, but only in C++ and not exposed
       // to js as far as I can tell
       var dataLen = data.length;
       var offset = 0;
@@ -400,52 +462,66 @@ TCPSocket.prototype = {
 
         var fragment = data.subarray(offset, offset + fragmentLen);
         offset += fragmentLen;
         result += String.fromCharCode.apply(null, fragment);
       }
       data = result;
     }
     var newBufferedAmount = this.bufferedAmount + data.length;
+    var bufferNotFull = newBufferedAmount < BUFFER_SIZE;
+    if (this._inChild) {
+      return bufferNotFull;
+    }
+
     new_stream.setData(data, data.length);
     this._multiplexStream.appendStream(new_stream);
 
     if (newBufferedAmount >= BUFFER_SIZE) {
       // If we buffered more than some arbitrary amount of data,
       // (65535 right now) we should tell the caller so they can
       // wait until ondrain is called if they so desire. Once all the
       //buffered data has been written to the socket, ondrain is
       // called.
       this._waitingForDrain = true;
     }
 
     this._ensureCopying();
-    return newBufferedAmount < BUFFER_SIZE;
+    return bufferNotFull;
   },
 
   suspend: function ts_suspend() {
+    if (this._inChild) {
+      this._socketBridge.suspend();
+      return;
+    }
+
     if (this._inputStreamPump) {
       this._inputStreamPump.suspend();
     } else {
       ++this._suspendCount;
     }
   },
 
   resume: function ts_resume() {
+    if (this._inChild) {
+      this._socketBridge.resume();
+      return;
+    }
+
     if (this._inputStreamPump) {
       this._inputStreamPump.resume();
     } else {
       --this._suspendCount;
     }
   },
 
   // nsITransportEventSink (Triggered by transport.setEventSink)
   onTransportStatus: function ts_onTransportStatus(
     transport, status, progress, max) {
-
     if (status === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
       this._readyState = kOPEN;
       this.callListener("onopen");
 
       this._inputStreamPump = new InputStreamPump(
         this._socketInputStream, -1, -1, 0, 0, false
       );
 
@@ -521,16 +597,17 @@ TCPSocket.prototype = {
       Ci.nsIObserver,
       Ci.nsISupportsWeakReference
     ],
     flags: Ci.nsIClassInfo.DOM_OBJECT,
   }),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIDOMTCPSocket,
+    Ci.nsITCPSocketInternal,
     Ci.nsIDOMGlobalPropertyInitializer,
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ])
 }
 
 
 function SecurityCallbacks(socket) {
--- a/dom/network/src/TCPSocket.manifest
+++ b/dom/network/src/TCPSocket.manifest
@@ -1,4 +1,8 @@
 # TCPSocket.js
 component {cda91b22-6472-11e1-aa11-834fec09cd0a} TCPSocket.js
 contract @mozilla.org/tcp-socket;1 {cda91b22-6472-11e1-aa11-834fec09cd0a}
 category JavaScript-navigator-property mozTCPSocket @mozilla.org/tcp-socket;1
+
+# TCPSocketParentIntermediary.js
+component {afa42841-a6cb-4a91-912f-93099f6a3d18} TCPSocketParentIntermediary.js
+contract @mozilla.org/tcp-socket-intermediary;1 {afa42841-a6cb-4a91-912f-93099f6a3d18}
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketChild.cpp
@@ -0,0 +1,209 @@
+/* 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 "TCPSocketChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/dom/PBrowserChild.h"
+#include "mozilla/dom/TabChild.h"
+#include "nsIDOMTCPSocket.h"
+#include "nsJSUtils.h"
+#include "nsContentUtils.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+using mozilla::net::gNeckoChild;
+
+namespace IPC {
+
+bool
+DeserializeUint8Array(JSRawObject aObj,
+                      const nsTArray<uint8_t>& aBuffer,
+                      jsval* aVal)
+{
+  JSContext* cx = nsContentUtils::GetSafeJSContext();
+  JSAutoRequest ar(cx);
+  JSAutoCompartment ac(cx, aObj);
+
+  JSObject* obj = JS_NewArrayBuffer(cx, aBuffer.Length());
+  if (!obj)
+    return false;
+  uint8_t* data = JS_GetArrayBufferData(obj, cx);
+  if (!data)
+    return false;
+  memcpy(data, aBuffer.Elements(), aBuffer.Length());
+  JSObject* arr = JS_NewUint8ArrayWithBuffer(cx, obj, 0, aBuffer.Length());
+  if (!arr)
+    return false;
+  *aVal = OBJECT_TO_JSVAL(arr);
+  return true;
+}
+
+} // namespace IPC
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_1(TCPSocketChildBase, mSocket)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketChildBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketChildBase)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketChildBase)
+  NS_INTERFACE_MAP_ENTRY(nsITCPSocketChild)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TCPSocketChildBase::TCPSocketChildBase()
+: mIPCOpen(false)
+{
+}
+
+TCPSocketChildBase::~TCPSocketChildBase()
+{
+}
+
+NS_IMETHODIMP_(nsrefcnt) TCPSocketChild::Release(void)
+{
+  nsrefcnt refcnt = TCPSocketChildBase::Release();
+  if (refcnt == 1 && mIPCOpen) {
+    PTCPSocketChild::SendRequestDelete();
+    return 1;
+  }
+  return refcnt;
+}
+
+TCPSocketChild::TCPSocketChild()
+: mSocketObj(nullptr)
+{
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Open(nsITCPSocketInternal* aSocket, const nsAString& aHost,
+                     uint16_t aPort, bool aUseSSL, const nsAString& aBinaryType,
+                     nsIDOMWindow* aWindow, const JS::Value& aSocketObj,
+                     JSContext* aCx)
+{
+  mSocket = aSocket;
+  MOZ_ASSERT(aSocketObj.isObject());
+  mSocketObj = &aSocketObj.toObject();
+  AddIPDLReference();
+  gNeckoChild->SendPTCPSocketConstructor(this, nsString(aHost), aPort,
+                                         aUseSSL, nsString(aBinaryType),
+                                         GetTabChildFrom(aWindow));
+  return NS_OK;
+}
+
+void
+TCPSocketChildBase::ReleaseIPDLReference()
+{
+  MOZ_ASSERT(mIPCOpen);
+  mIPCOpen = false;
+  this->Release();
+}
+
+void
+TCPSocketChildBase::AddIPDLReference()
+{
+  MOZ_ASSERT(!mIPCOpen);
+  mIPCOpen = true;
+  this->AddRef();
+}
+
+TCPSocketChild::~TCPSocketChild()
+{
+}
+
+bool
+TCPSocketChild::RecvCallback(const nsString& aType,
+                             const CallbackData& aData,
+                             const nsString& aReadyState,
+                             const uint32_t& aBuffered)
+{
+  if (NS_FAILED(mSocket->UpdateReadyStateAndBuffered(aReadyState, aBuffered)))
+    NS_ERROR("Shouldn't fail!");
+
+  nsresult rv = NS_ERROR_FAILURE;
+  if (aData.type() == CallbackData::Tvoid_t) {
+    rv = mSocket->CallListenerVoid(aType);
+
+  } else if (aData.type() == CallbackData::TJSError) {
+    const JSError& err(aData.get_JSError());
+    rv = mSocket->CallListenerError(aType, err.message(), err.filename(),
+                                   err.lineNumber(), err.columnNumber());
+
+  } else if (aData.type() == CallbackData::TSendableData) {
+    const SendableData& data = aData.get_SendableData();
+
+    if (data.type() == SendableData::TArrayOfuint8_t) {
+      jsval val;
+      IPC::DeserializeUint8Array(mSocketObj, data.get_ArrayOfuint8_t(), &val);
+      rv = mSocket->CallListenerArrayBuffer(aType, val);
+
+    } else if (data.type() == SendableData::TnsString) {
+      rv = mSocket->CallListenerData(aType, data.get_nsString());
+
+    } else {
+      MOZ_NOT_REACHED("Invalid callback data type!");
+    }
+
+  } else {
+    MOZ_NOT_REACHED("Invalid callback type!");
+  }
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Suspend()
+{
+  SendSuspend();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Resume()
+{
+  SendResume();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Close()
+{
+  SendClose();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Send(const JS::Value& aData, JSContext* aCx)
+{
+  if (aData.isString()) {
+    JSString* jsstr = aData.toString();
+    nsDependentJSString str;
+    bool ok = str.init(aCx, jsstr);
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+    SendData(str);
+
+  } else {
+    NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_FAILURE);
+    JSObject* obj = &aData.toObject();
+    NS_ENSURE_TRUE(JS_IsTypedArrayObject(obj, aCx), NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(JS_IsUint8Array(obj, aCx), NS_ERROR_FAILURE);
+    uint32_t nbytes = JS_GetTypedArrayByteLength(obj, aCx);
+    uint8_t* data = JS_GetUint8ArrayData(obj, aCx);
+    if (!data) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    FallibleTArray<uint8_t> fallibleArr;
+    if (!fallibleArr.InsertElementsAt(0, data, nbytes)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    InfallibleTArray<uint8_t> arr;
+    arr.SwapElements(fallibleArr);
+    SendData(arr);
+  }
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketChild.h
@@ -0,0 +1,55 @@
+/* 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 "mozilla/net/PTCPSocketChild.h"
+#include "nsITCPSocketChild.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+
+#define TCPSOCKETCHILD_CID \
+  { 0xa589d96f, 0x7e09, 0x4edf, { 0xa0, 0x1a, 0xeb, 0x49, 0x51, 0xf4, 0x2f, 0x37 } }
+
+class nsITCPSocketInternal;
+class JSContext;
+class JSObject;
+
+namespace mozilla {
+namespace dom {
+
+class TCPSocketChildBase : public nsITCPSocketChild {
+public:
+  NS_DECL_CYCLE_COLLECTION_CLASS(TCPSocketChildBase)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+  void AddIPDLReference();
+  void ReleaseIPDLReference();
+
+protected:
+  TCPSocketChildBase();
+  virtual ~TCPSocketChildBase();
+
+  nsCOMPtr<nsITCPSocketInternal> mSocket;
+  bool mIPCOpen;
+};
+
+class TCPSocketChild : public mozilla::net::PTCPSocketChild
+                     , public TCPSocketChildBase
+{
+public:
+  NS_DECL_NSITCPSOCKETCHILD
+  NS_IMETHOD_(nsrefcnt) Release() MOZ_OVERRIDE;
+
+  TCPSocketChild();
+  ~TCPSocketChild();
+
+  virtual bool RecvCallback(const nsString& aType,
+                            const CallbackData& aData,
+                            const nsString& aReadyState,
+                            const uint32_t& aBuffered) MOZ_OVERRIDE;
+private:
+  JSObject* mSocketObj;
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketParent.cpp
@@ -0,0 +1,244 @@
+/* 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 "TCPSocketParent.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "nsJSUtils.h"
+#include "nsIDOMTCPSocket.h"
+#include "mozilla/unused.h"
+#include "mozilla/AppProcessPermissions.h"
+
+namespace IPC {
+
+//Defined in TCPSocketChild.cpp
+extern bool
+DeserializeUint8Array(JSRawObject aObj,
+                      const nsTArray<uint8_t>& aBuffer,
+                      jsval* aVal);
+
+}
+
+namespace mozilla {
+namespace dom {
+
+static void
+FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo)
+{
+  mozilla::unused <<
+      aActor->SendCallback(NS_LITERAL_STRING("onerror"),
+                          JSError(NS_LITERAL_STRING("Internal error"),
+                                  NS_LITERAL_STRING(__FILE__), aLineNo, 0),
+                          NS_LITERAL_STRING("connecting"), 0);
+}
+
+NS_IMPL_CYCLE_COLLECTION_2(TCPSocketParent, mSocket, mIntermediary)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketParent)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketParent)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketParent)
+  NS_INTERFACE_MAP_ENTRY(nsITCPSocketParent)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+bool
+TCPSocketParent::Init(const nsString& aHost, const uint16_t& aPort, const bool& aUseSSL,
+                      const nsString& aBinaryType, PBrowserParent* aBrowser)
+{
+  // We don't have browser actors in xpcshell, and hence can't run automated
+  // tests without this loophole.
+  if (aBrowser && !AssertAppProcessPermission(aBrowser, "tcp-socket")) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  nsresult rv;
+  mIntermediary = do_CreateInstance("@mozilla.org/tcp-socket-intermediary;1", &rv);
+  if (NS_FAILED(rv)) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  rv = mIntermediary->Open(this, aHost, aPort, aUseSSL, aBinaryType, getter_AddRefs(mSocket));
+  if (NS_FAILED(rv) || !mSocket) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  return true;
+}
+
+NS_IMETHODIMP
+TCPSocketParent::InitJS(const JS::Value& aIntermediary, JSContext* aCx)
+{
+  MOZ_ASSERT(aIntermediary.isObject());
+  mIntermediaryObj = &aIntermediary.toObject();
+  return NS_OK;
+}
+
+bool
+TCPSocketParent::RecvSuspend()
+{
+  NS_ENSURE_TRUE(mSocket, true);
+  nsresult rv = mSocket->Suspend();
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+bool
+TCPSocketParent::RecvResume()
+{
+  NS_ENSURE_TRUE(mSocket, true);
+  nsresult rv = mSocket->Resume();
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+bool
+TCPSocketParent::RecvData(const SendableData& aData)
+{
+  NS_ENSURE_TRUE(mIntermediary, true);
+
+  nsresult rv;
+  switch (aData.type()) {
+    case SendableData::TArrayOfuint8_t: {
+      jsval val;
+      IPC::DeserializeUint8Array(mIntermediaryObj, aData.get_ArrayOfuint8_t(), &val);
+      rv = mIntermediary->SendArrayBuffer(val);
+      NS_ENSURE_SUCCESS(rv, true);
+      break;
+    }
+
+    case SendableData::TnsString:
+      rv = mIntermediary->SendString(aData.get_nsString());
+      NS_ENSURE_SUCCESS(rv, true);
+      break;
+
+    default:
+      MOZ_NOT_REACHED();
+      return false;
+  }
+  return true;
+}
+
+bool
+TCPSocketParent::RecvClose()
+{
+  NS_ENSURE_TRUE(mSocket, true);
+  nsresult rv = mSocket->Close();
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+NS_IMETHODIMP
+TCPSocketParent::SendCallback(const nsAString& aType, const JS::Value& aDataVal,
+                              const nsAString& aReadyState, uint32_t aBuffered,
+                              JSContext* aCx)
+{
+  if (!mIPCOpen) {
+    NS_WARNING("Dropping callback due to no IPC connection");
+    return NS_OK;
+  }
+
+  CallbackData data;
+  if (aDataVal.isString()) {
+    JSString* jsstr = aDataVal.toString();
+    nsDependentJSString str;
+    if (!str.init(aCx, jsstr)) {
+      FireInteralError(this, __LINE__);
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    data = str;
+
+  } else if (aDataVal.isUndefined() || aDataVal.isNull()) {
+    data = mozilla::void_t();
+
+  } else if (aDataVal.isObject()) {
+    JSObject* obj = &aDataVal.toObject();
+    if (JS_IsTypedArrayObject(obj, aCx)) {
+      NS_ENSURE_TRUE(JS_IsUint8Array(obj, aCx), NS_ERROR_FAILURE);
+      uint32_t nbytes = JS_GetTypedArrayByteLength(obj, aCx);
+      uint8_t* buffer = JS_GetUint8ArrayData(obj, aCx);
+      if (!buffer) {
+        FireInteralError(this, __LINE__);
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      FallibleTArray<uint8_t> fallibleArr;
+      if (!fallibleArr.InsertElementsAt(0, buffer, nbytes)) {
+        FireInteralError(this, __LINE__);
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      InfallibleTArray<uint8_t> arr;
+      arr.SwapElements(fallibleArr);
+      data = SendableData(arr);
+
+    } else {
+      nsDependentJSString message, filename;
+      uint32_t lineNumber = 0;
+      uint32_t columnNumber = 0;
+
+      jsval val;
+      if (!JS_GetProperty(aCx, obj, "message", &val)) {
+        NS_ERROR("No message property on supposed error object");
+      } else if (JSVAL_IS_STRING(val)) {
+        if (!message.init(aCx, JSVAL_TO_STRING(val))) {
+          NS_WARNING("couldn't initialize string");
+        }
+      }
+
+      if (!JS_GetProperty(aCx, obj, "fileName", &val)) {
+        NS_ERROR("No fileName property on supposed error object");
+      } else if (JSVAL_IS_STRING(val)) {
+        if (!filename.init(aCx, JSVAL_TO_STRING(val))) {
+          NS_WARNING("couldn't initialize string");
+        }
+      }
+
+      if (!JS_GetProperty(aCx, obj, "lineNumber", &val)) {
+        NS_ERROR("No lineNumber property on supposed error object");
+      } else if (JSVAL_IS_INT(val)) {
+        lineNumber = JSVAL_TO_INT(val);
+      }
+
+      if (!JS_GetProperty(aCx, obj, "columnNumber", &val)) {
+        NS_ERROR("No columnNumber property on supposed error object");
+      } else if (JSVAL_IS_INT(val)) {
+        columnNumber = JSVAL_TO_INT(val);
+      }
+
+      data = JSError(message, filename, lineNumber, columnNumber);
+    }
+  } else {
+    NS_ERROR("Unexpected JS value encountered");
+    FireInteralError(this, __LINE__);
+    return NS_ERROR_FAILURE;
+  }
+  mozilla::unused <<
+      PTCPSocketParent::SendCallback(nsString(aType), data,
+                                     nsString(aReadyState), aBuffered);
+  return NS_OK;
+}
+
+void
+TCPSocketParent::ActorDestroy(ActorDestroyReason why)
+{
+  MOZ_ASSERT(mIPCOpen);
+  mIPCOpen = false;
+  if (mSocket) {
+    mSocket->Close();
+  }
+  mSocket = nullptr;
+  mIntermediaryObj = nullptr;
+  mIntermediary = nullptr;
+}
+
+bool
+TCPSocketParent::RecvRequestDelete()
+{
+  mozilla::unused << Send__delete__(this);
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketParent.h
@@ -0,0 +1,49 @@
+/* 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 "mozilla/net/PTCPSocketParent.h"
+#include "nsITCPSocketParent.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+#include "nsIDOMTCPSocket.h"
+
+class JSContext;
+class JSObject;
+
+namespace mozilla {
+namespace dom {
+
+class PBrowserParent;
+
+class TCPSocketParent : public mozilla::net::PTCPSocketParent
+                      , public nsITCPSocketParent
+{
+public:
+  NS_DECL_CYCLE_COLLECTION_CLASS(TCPSocketParent)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSITCPSOCKETPARENT
+
+  TCPSocketParent() : mIntermediaryObj(nullptr), mIPCOpen(true) {}
+
+  bool Init(const nsString& aHost, const uint16_t& aPort,
+            const bool& useSSL, const nsString& aBinaryType,
+            PBrowserParent* aBrowser);
+
+  virtual bool RecvSuspend() MOZ_OVERRIDE;
+  virtual bool RecvResume() MOZ_OVERRIDE;
+  virtual bool RecvClose() MOZ_OVERRIDE;
+  virtual bool RecvData(const SendableData& aData) MOZ_OVERRIDE;
+  virtual bool RecvRequestDelete() MOZ_OVERRIDE;
+
+private:
+  virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+
+  nsCOMPtr<nsITCPSocketIntermediary> mIntermediary;
+  nsCOMPtr<nsIDOMTCPSocket> mSocket;
+  JSObject* mIntermediaryObj;
+  bool mIPCOpen;
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketParentIntermediary.js
@@ -0,0 +1,55 @@
+/* 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/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function TCPSocketParentIntermediary() {
+}
+
+TCPSocketParentIntermediary.prototype = {
+  open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType) {
+    aParentSide.initJS(this);
+
+    let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
+    let socket = this._socket = baseSocket.open(aHost, aPort,
+                                                {useSSL: aUseSSL,
+                                                binaryType: aBinaryType});
+    if (!socket)
+      return null;
+
+    // Create handlers for every possible callback that attempt to trigger
+    // corresponding callbacks on the child object.
+    ["onopen", "ondrain", "ondata", "onerror", "onclose"].forEach(
+      function(p) {
+        socket[p] = function(data) {
+          aParentSide.sendCallback(p, data.data, socket.readyState,
+                                   socket.bufferedAmount);
+        };
+      }
+    );
+
+    return socket;
+  },
+
+  sendString: function(aData) {
+    return this._socket.send(aData);
+  },
+
+  sendArrayBuffer: function(aData) {
+    return this._socket.send(aData);
+  },
+
+  classID: Components.ID("{afa42841-a6cb-4a91-912f-93099f6a3d18}"),
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsITCPSocketIntermediary
+  ])
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([TCPSocketParentIntermediary]);
new file mode 100644
--- /dev/null
+++ b/dom/network/src/ipdl.mk
@@ -0,0 +1,7 @@
+# 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/.
+
+IPDLSRCS = \
+  PTCPSocket.ipdl \
+  $(NULL)
--- a/dom/network/tests/Makefile.in
+++ b/dom/network/tests/Makefile.in
@@ -18,11 +18,11 @@ MOCHITEST_FILES = \
   test_network_basics.html \
   test_tcpsocket_default_permissions.html \
   test_tcpsocket_enabled_no_perm.html \
   test_tcpsocket_enabled_with_perm.html \
   $(NULL)
 
 MODULE = test_dom_socket
 
-XPCSHELL_TESTS = unit
+XPCSHELL_TESTS = unit unit_ipc
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/network/tests/unit/test_tcpsocket.js
+++ b/dom/network/tests/unit/test_tcpsocket.js
@@ -51,16 +51,21 @@ const ServerSocket = CC("@mozilla.org/ne
                              "nsIBinaryInputStream",
                              "setInputStream"),
       BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
                               "nsIBinaryOutputStream",
                               "setOutputStream"),
       TCPSocket = new (CC("@mozilla.org/tcp-socket;1",
                      "nsIDOMTCPSocket"))();
 
+const gInChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
+                  .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
 /**
  *
  * Helper functions
  *
  */
 
 /**
  * Spin up a listening socket and associate at most one live, accepted socket
@@ -361,17 +366,23 @@ function bufferedClose() {
 function badConnect() {
   // There's probably nothing listening on tcp port 2.
   sock = TCPSocket.open('127.0.0.1', 2);
 
   sock.onopen = makeFailureCase('open');
   sock.ondata = makeFailureCase('data');
   sock.onclose = makeFailureCase('close');
 
-  sock.onerror = makeSuccessCase('error');
+  let success = makeSuccessCase('error');
+  sock.onerror = function(data) {
+    do_check_neq(data.data.message, '');
+    do_check_neq(data.data.fileName, '');
+    do_check_neq(data.data.lineNumber, 0);
+    success();
+  };
 }
 
 /**
  * Test that calling send with enough data to buffer causes ondrain to
  * be invoked once the data has been sent, and then test that calling send
  * and buffering again causes ondrain to be fired again.
  */
 
@@ -405,16 +416,18 @@ function drainTwice() {
   if (sock.send(BIG_TYPED_ARRAY)) {
     throw new Error("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering");
   }
 }
 
 function cleanup() {
   do_print("Cleaning up");
   sock.close();
+  if (!gInChild)
+    Services.prefs.clearUserPref('dom.mozTCPSocket.enabled');
   run_next_test();
 }
 
 /**
  * Test that calling send with enough data to buffer twice in a row without
  * waiting for ondrain still results in ondrain being invoked at least once.
  */
 
@@ -468,16 +481,19 @@ add_test(drainTwice);
 // send a buffer, get a drain, send a buffer, get a drain
 add_test(connectSock);
 add_test(bufferTwice);
 
 // clean up
 add_test(cleanup);
 
 function run_test() {
+  if (!gInChild)
+    Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
+  
   server = new TestServer();
 
   run_next_test();
 
   do_timeout(10000, function() {
     do_throw(
       "The test should never take this long unless the system is hosed.");
   });
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/unit_ipc/test_tcpsocket_ipc.js
@@ -0,0 +1,9 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function run_test() {
+  Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
+  run_test_in_child("../unit/test_tcpsocket.js", function() {
+    Services.prefs.clearUserPref('dom.mozTCPSocket.enabled');
+    do_test_finished();
+  });
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/unit_ipc/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_tcpsocket_ipc.js]
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -25,16 +25,17 @@ IPDLDIRS =  \
   uriloader/exthandler \
   dom/devicestorage \
   dom/indexedDB/ipc \
   dom/bluetooth/ipc \
   dom/plugins/ipc  \
   dom/ipc \
   dom/sms/src/ipc \
   dom/src/storage \
+  dom/network/src \
   gfx/layers/ipc \
   hal/sandbox \
   ipc/glue  \
   ipc/testshell  \
   js/ipc  \
   layout/ipc \
   netwerk/ipc  \
   netwerk/protocol/ftp \
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -88,16 +88,17 @@
 #include "nsIControllerContext.h"
 #include "nsDOMScriptObjectFactory.h"
 #include "nsDOMStorage.h"
 #include "nsJSON.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/OSFileConstants.h"
 #include "mozilla/dom/Activity.h"
+#include "mozilla/dom/network/TCPSocketChild.h"
 
 #ifdef MOZ_B2G_RIL
 #include "SystemWorkerManager.h"
 using mozilla::dom::gonk::SystemWorkerManager;
 #define SYSTEMWORKERMANAGER_CID \
   {0xd53b6524, 0x6ac3, 0x42b0, {0xae, 0xca, 0x62, 0xb3, 0xc4, 0xe5, 0x2b, 0x04}}
 #define SYSTEMWORKERMANAGER_CONTRACTID \
   "@mozilla.org/telephony/system-worker-manager;1"
@@ -243,16 +244,17 @@ static void Shutdown();
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::file;
 using namespace mozilla::dom::sms;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::dom::power::PowerManagerService;
+using mozilla::dom::TCPSocketChild;
 
 
 // Transformiix
 /* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
 #define TRANSFORMIIX_NODESET_CID \
 { 0x5d5d92cd, 0x6bf8, 0x11d9, { 0xbf, 0x4a, 0x0, 0x0a, 0x95, 0xdc, 0x23, 0x4c } }
 
 #define TRANSFORMIIX_NODESET_CONTRACTID \
@@ -652,16 +654,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMixedCo
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsPrincipal)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecurityNameSet)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsSystemPrincipal,
     nsScriptSecurityManager::SystemPrincipalSingletonConstructor)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNullPrincipal, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsStructuredCloneContainer)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(OSFileConstantsService)
+NS_GENERIC_FACTORY_CONSTRUCTOR(TCPSocketChild)
 
 static nsresult
 Construct_nsIScriptSecurityManager(nsISupports *aOuter, REFNSIID aIID, 
                                    void **aResult)
 {
     if (!aResult)
         return NS_ERROR_NULL_POINTER;
     *aResult = nullptr;
@@ -819,16 +822,17 @@ NS_DEFINE_NAMED_CID(NS_HAPTICFEEDBACK_CI
 #endif
 #endif
 NS_DEFINE_NAMED_CID(SMS_SERVICE_CID);
 NS_DEFINE_NAMED_CID(SMS_DATABASE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(SMS_REQUEST_MANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_POWERMANAGERSERVICE_CID);
 NS_DEFINE_NAMED_CID(OSFILECONSTANTSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_ALARMHALSERVICE_CID);
+NS_DEFINE_NAMED_CID(TCPSOCKETCHILD_CID);
 
 static nsresult
 CreateWindowCommandTableConstructor(nsISupports *aOuter,
                                     REFNSIID aIID, void **aResult)
 {
   nsresult rv;
   nsCOMPtr<nsIControllerCommandTable> commandTable =
       do_CreateInstance(NS_CONTROLLERCOMMANDTABLE_CONTRACTID, &rv);
@@ -1097,16 +1101,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_STRUCTUREDCLONECONTAINER_CID, false, NULL, nsStructuredCloneContainerConstructor },
   { &kNS_DOMMUTATIONOBSERVER_CID, false, NULL, nsDOMMutationObserverConstructor },
   { &kSMS_SERVICE_CID, false, NULL, nsISmsServiceConstructor },
   { &kSMS_DATABASE_SERVICE_CID, false, NULL, nsISmsDatabaseServiceConstructor },
   { &kSMS_REQUEST_MANAGER_CID, false, NULL, SmsRequestManagerConstructor },
   { &kNS_POWERMANAGERSERVICE_CID, false, NULL, nsIPowerManagerServiceConstructor },
   { &kOSFILECONSTANTSSERVICE_CID, true, NULL, OSFileConstantsServiceConstructor },
   { &kNS_ALARMHALSERVICE_CID, false, NULL, nsIAlarmHalServiceConstructor },
+  { &kTCPSOCKETCHILD_CID, false, NULL, TCPSocketChildConstructor },
   { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
   XPCONNECT_CONTRACTS
   { "@mozilla.org/layout/xul-boxobject;1", &kNS_BOXOBJECT_CID },
 #ifdef MOZ_XUL
   { "@mozilla.org/layout/xul-boxobject-listbox;1", &kNS_LISTBOXOBJECT_CID },
@@ -1240,16 +1245,17 @@ static const mozilla::Module::ContractID
   { NS_STRUCTUREDCLONECONTAINER_CONTRACTID, &kNS_STRUCTUREDCLONECONTAINER_CID },
   { NS_DOMMUTATIONOBSERVER_CONTRACTID, &kNS_DOMMUTATIONOBSERVER_CID },
   { SMS_SERVICE_CONTRACTID, &kSMS_SERVICE_CID },
   { SMS_DATABASE_SERVICE_CONTRACTID, &kSMS_DATABASE_SERVICE_CID },
   { SMS_REQUEST_MANAGER_CONTRACTID, &kSMS_REQUEST_MANAGER_CID },
   { POWERMANAGERSERVICE_CONTRACTID, &kNS_POWERMANAGERSERVICE_CID },
   { OSFILECONSTANTSSERVICE_CONTRACTID, &kOSFILECONSTANTSSERVICE_CID },
   { ALARMHALSERVICE_CONTRACTID, &kNS_ALARMHALSERVICE_CID },
+  { "@mozilla.org/tcp-socket-child;1", &kTCPSOCKETCHILD_CID },
   { NULL }
 };
 
 static const mozilla::Module::CategoryEntry kLayoutCategories[] = {
   XPCONNECT_CATEGORIES
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY, "Image", NS_HTMLIMGELEMENT_CONTRACTID },
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_PROTO_ALIAS_CATEGORY, "Image", "HTMLImageElement" },
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY, "Option", NS_HTMLOPTIONELEMENT_CONTRACTID },
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -345,16 +345,17 @@
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
 
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 ; Modules
 @BINPATH@/modules/*
 
 #ifdef MOZ_SAFE_BROWSING
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
--- a/mobile/xul/installer/package-manifest.in
+++ b/mobile/xul/installer/package-manifest.in
@@ -432,16 +432,17 @@
 @BINPATH@/components/Weave.js
 @BINPATH@/components/WeaveCrypto.manifest
 @BINPATH@/components/WeaveCrypto.js
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
 @BINPATH@/components/nsUrlClassifierHashCompleter.js
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -8,16 +8,19 @@
 #include "nsHttp.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/WyciwygChannelChild.h"
 #include "mozilla/net/FTPChannelChild.h"
 #include "mozilla/net/WebSocketChannelChild.h"
+#include "mozilla/dom/network/TCPSocketChild.h"
+
+using mozilla::dom::TCPSocketChild;
 
 namespace mozilla {
 namespace net {
 
 PNeckoChild *gNeckoChild = nullptr;
 
 // C++ file contents
 NeckoChild::NeckoChild()
@@ -144,10 +147,29 @@ NeckoChild::AllocPWebSocket(PBrowserChil
 bool
 NeckoChild::DeallocPWebSocket(PWebSocketChild* child)
 {
   WebSocketChannelChild* p = static_cast<WebSocketChannelChild*>(child);
   p->ReleaseIPDLReference();
   return true;
 }
 
+PTCPSocketChild*
+NeckoChild::AllocPTCPSocket(const nsString& aHost,
+                            const uint16_t& aPort,
+                            const bool& useSSL,
+                            const nsString& aBinaryType,
+                            PBrowserChild* aBrowser)
+{
+  NS_NOTREACHED("AllocPTCPSocket should not be called");
+  return nullptr;
+}
+
+bool
+NeckoChild::DeallocPTCPSocket(PTCPSocketChild* child)
+{
+  TCPSocketChild* p = static_cast<TCPSocketChild*>(child);
+  p->ReleaseIPDLReference();
+  return true;
+}
+
 }} // mozilla::net
 
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -31,16 +31,22 @@ protected:
   virtual PCookieServiceChild* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceChild*);
   virtual PWyciwygChannelChild* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelChild*);
   virtual PFTPChannelChild* AllocPFTPChannel();
   virtual bool DeallocPFTPChannel(PFTPChannelChild*);
   virtual PWebSocketChild* AllocPWebSocket(PBrowserChild*);
   virtual bool DeallocPWebSocket(PWebSocketChild*);
+  virtual PTCPSocketChild* AllocPTCPSocket(const nsString& aHost,
+                                           const uint16_t& aPort,
+                                           const bool& useSSL,
+                                           const nsString& aBinaryType,
+                                           PBrowserChild* aBrowser);
+  virtual bool DeallocPTCPSocket(PTCPSocketChild*);
 };
 
 /**
  * Reference to the PNecko Child protocol.
  * Null if this is not a content process.
  */
 extern PNeckoChild *gNeckoChild;
 
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -8,20 +8,23 @@
 #include "nsHttp.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/net/WyciwygChannelParent.h"
 #include "mozilla/net/FTPChannelParent.h"
 #include "mozilla/net/WebSocketChannelParent.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/network/TCPSocketParent.h"
 
 #include "nsHTMLDNSPrefetch.h"
 
 using mozilla::dom::TabParent;
+using mozilla::net::PTCPSocketParent;
+using mozilla::dom::TCPSocketParent;
 
 namespace mozilla {
 namespace net {
 
 // C++ file contents
 NeckoParent::NeckoParent()
 {
 }
@@ -103,16 +106,48 @@ NeckoParent::AllocPWebSocket(PBrowserPar
 bool
 NeckoParent::DeallocPWebSocket(PWebSocketParent* actor)
 {
   WebSocketChannelParent* p = static_cast<WebSocketChannelParent*>(actor);
   p->Release();
   return true;
 }
 
+PTCPSocketParent*
+NeckoParent::AllocPTCPSocket(const nsString& aHost,
+                             const uint16_t& aPort,
+                             const bool& useSSL,
+                             const nsString& aBinaryType,
+                             PBrowserParent* aBrowser)
+{
+  TCPSocketParent* p = new TCPSocketParent();
+  p->AddRef();
+  return p;
+}
+
+bool
+NeckoParent::RecvPTCPSocketConstructor(PTCPSocketParent* aActor,
+                                       const nsString& aHost,
+                                       const uint16_t& aPort,
+                                       const bool& useSSL,
+                                       const nsString& aBinaryType,
+                                       PBrowserParent* aBrowser)
+{
+  return static_cast<TCPSocketParent*>(aActor)->
+      Init(aHost, aPort, useSSL, aBinaryType, aBrowser);
+}
+
+bool
+NeckoParent::DeallocPTCPSocket(PTCPSocketParent* actor)
+{
+  TCPSocketParent* p = static_cast<TCPSocketParent*>(actor);
+  p->Release();
+  return true;
+}
+
 bool
 NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname,
                                  const uint16_t& flags)
 {
   nsHTMLDNSPrefetch::Prefetch(hostname, flags);
   return true;
 }
 
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -28,16 +28,28 @@ protected:
   virtual PCookieServiceParent* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceParent*);
   virtual PWyciwygChannelParent* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelParent*);
   virtual PFTPChannelParent* AllocPFTPChannel();
   virtual bool DeallocPFTPChannel(PFTPChannelParent*);
   virtual PWebSocketParent* AllocPWebSocket(PBrowserParent* browser);
   virtual bool DeallocPWebSocket(PWebSocketParent*);
+  virtual PTCPSocketParent* AllocPTCPSocket(const nsString& aHost,
+                                            const uint16_t& aPort,
+                                            const bool& useSSL,
+                                            const nsString& aBinaryType,
+                                            PBrowserParent* aBrowser);
+  virtual bool RecvPTCPSocketConstructor(PTCPSocketParent*,
+                                         const nsString& aHost,
+                                         const uint16_t& aPort,
+                                         const bool& useSSL,
+                                         const nsString& aBinaryType,
+                                         PBrowserParent* aBrowser);
+  virtual bool DeallocPTCPSocket(PTCPSocketParent*);
   virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
                                    const uint16_t& flags);
   virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
                                          const uint16_t& flags,
                                          const nsresult& reason);
 
 };
 
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -7,38 +7,42 @@
 
 include protocol PContent;
 include protocol PHttpChannel;
 include protocol PCookieService;
 include protocol PBrowser;
 include protocol PWyciwygChannel;
 include protocol PFTPChannel;
 include protocol PWebSocket;
+include protocol PTCPSocket;
 
 namespace mozilla {
 namespace net {
 
 
 //-------------------------------------------------------------------
 sync protocol PNecko
 {
   manager PContent;
   manages PHttpChannel;
   manages PCookieService;
   manages PWyciwygChannel;
   manages PFTPChannel;
   manages PWebSocket;
+  manages PTCPSocket;
 
 parent:
   __delete__();
 
   PCookieService();
   PWyciwygChannel();
   PFTPChannel();
   PWebSocket(PBrowser browser);
+  PTCPSocket(nsString host, uint16_t port, bool useSSL, nsString binaryType,
+             nullable PBrowser browser);
 
   HTMLDNSPrefetch(nsString hostname, uint16_t flags);
   CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
 
 both:
   PHttpChannel(nullable PBrowser browser);
 };
 
--- a/testing/xpcshell/xpcshell.ini
+++ b/testing/xpcshell/xpcshell.ini
@@ -8,16 +8,17 @@
 [include:modules/libjar/zipwriter/test/unit/xpcshell.ini]
 [include:uriloader/exthandler/tests/unit/xpcshell.ini]
 [include:parser/xml/test/unit/xpcshell.ini]
 [include:image/test/unit/xpcshell.ini]
 [include:dom/plugins/test/unit/xpcshell.ini]
 [include:dom/sms/tests/xpcshell.ini]
 [include:dom/mms/tests/xpcshell.ini]
 [include:dom/network/tests/unit/xpcshell.ini]
+[include:dom/network/tests/unit_ipc/xpcshell.ini]
 [include:dom/src/json/test/unit/xpcshell.ini]
 [include:dom/system/gonk/tests/xpcshell.ini]
 [include:dom/tests/unit/xpcshell.ini]
 [include:dom/indexedDB/test/unit/xpcshell.ini]
 [include:content/xtf/test/unit/xpcshell.ini]
 [include:docshell/test/unit/xpcshell.ini]
 [include:docshell/test/unit_ipc/xpcshell.ini]
 [include:embedding/tests/unit/xpcshell.ini]