Bug 770778 - Make TCPSocket e10s-friendly. r=bent
authorJosh Matthews <josh@joshmatthews.net>
Mon, 24 Sep 2012 14:53:49 -0400
changeset 115214 806fa3680a9138de39b0441ef2bb139069c08772
parent 115213 60d3d0e84b2b2b454b1a80fce7cbf1fec40f2560
child 115215 519f41fa596f7e3ef09ea28f28ffcad390e45c01
push id239
push userakeybl@mozilla.com
push dateThu, 03 Jan 2013 21:54:43 +0000
treeherdermozilla-release@3a7b66445659 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs770778
milestone18.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 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]