Bug 885982 - Convert MozTCPSocket to WebIDL. r=baku
☠☠ backed out by 0c454540fc2b ☠ ☠
authorJosh Matthews <josh@joshmatthews.net>
Mon, 24 Jun 2013 16:50:00 -0400
changeset 251534 9ede577f5ada3f664d7a9bc13d2f617901349e00
parent 251533 6ccc86f7429e38dec4343a43f7d994c4c553e91e
child 251535 865e7bc208dfde6b92fdfd074995728de78189f7
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs885982
milestone38.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 885982 - Convert MozTCPSocket to WebIDL. r=baku
CLOBBER
dom/bindings/Bindings.conf
dom/network/TCPServerSocket.js
dom/network/TCPServerSocketParent.cpp
dom/network/TCPServerSocketParent.h
dom/network/TCPSocket.js
dom/network/TCPSocket.manifest
dom/network/TCPSocketChild.cpp
dom/network/TCPSocketParent.cpp
dom/network/TCPSocketParent.h
dom/network/TCPSocketParentIntermediary.js
dom/network/TCPSocketUtils.cpp
dom/network/TCPSocketUtils.h
dom/network/interfaces/moz.build
dom/network/interfaces/nsIDOMTCPSocket.idl
dom/network/interfaces/nsITCPSocketParent.idl
dom/network/moz.build
dom/network/tests/mochitest.ini
dom/network/tests/test_tcpsocket_client_and_server_basics.js
dom/network/tests/test_tcpsocket_default_permissions.html
dom/network/tests/test_tcpsocket_enabled_no_perm.html
dom/network/tests/test_tcpsocket_enabled_with_perm.html
dom/network/tests/unit/test_tcpsocket.js
dom/network/tests/unit_ipc/test_tcpsocket_ipc.js
dom/permission/tests/test_tcp-socket.html
dom/webidl/TCPServerSocket.webidl
dom/webidl/TCPServerSocketEvent.webidl
dom/webidl/TCPSocket.webidl
dom/webidl/TCPSocketEvent.webidl
dom/webidl/moz.build
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1121297 - Converted VolatileBuffer's CPP tests to GTests
+Renaming nsIDOMTCPSocket.idl and nsIDOMTCPServerSocket.idl for bug 885982 and bug 1118063.
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1861,30 +1861,30 @@ addExternalIface('MozTreeView', nativeTy
                   headerFile='nsITreeView.h')
 addExternalIface('MozWakeLockListener', headerFile='nsIDOMWakeLockListener.h')
 addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder')
 addExternalIface('nsIBrowserDOMWindow', nativeType='nsIBrowserDOMWindow',
                  notflattened=True)
 addExternalIface('nsIControllers', nativeType='nsIControllers')
 addExternalIface('nsIDOMCrypto', nativeType='nsIDOMCrypto',
                  headerFile='Crypto.h')
-addExternalIface('nsIDOMTCPSocket', nativeType='nsIDOMTCPSocket', notflattened=True)
 addExternalIface('nsIInputStreamCallback', nativeType='nsIInputStreamCallback',
                  headerFile='nsIAsyncInputStream.h')
 addExternalIface('nsIFile', nativeType='nsIFile', notflattened=True)
 addExternalIface('nsIMessageBroadcaster', nativeType='nsIMessageBroadcaster',
                  headerFile='nsIMessageManager.h', notflattened=True)
 addExternalIface('nsISelectionListener', nativeType='nsISelectionListener')
 addExternalIface('nsIStreamListener', nativeType='nsIStreamListener', notflattened=True)
 addExternalIface('nsISupports', nativeType='nsISupports')
 addExternalIface('nsIDocShell', nativeType='nsIDocShell', notflattened=True)
 addExternalIface('nsIEditor', nativeType='nsIEditor', notflattened=True)
 addExternalIface('nsIVariant', nativeType='nsIVariant', notflattened=True)
 addExternalIface('nsIScriptableRegion', nativeType='nsIScriptableRegion', notflattened=True)
 addExternalIface('nsITCPServerSocketInternal', nativeType='nsITCPServerSocketInternal', notflattened=True)
+addExternalIface('nsITCPSocketInternal', nativeType='nsITCPSocketInternal', notflattened=True)
 addExternalIface('OutputStream', nativeType='nsIOutputStream',
                  notflattened=True)
 addExternalIface('Principal', nativeType='nsIPrincipal',
                  headerFile='nsIPrincipal.h', notflattened=True)
 addExternalIface('StackFrame', nativeType='nsIStackFrame',
                  headerFile='nsIException.h', notflattened=True)
 addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
                  notflattened=True)
--- a/dom/network/TCPServerSocket.js
+++ b/dom/network/TCPServerSocket.js
@@ -144,17 +144,18 @@ TCPServerSocket.prototype = {
       else {
         throw new Error("Parent TCPServerSocket has already listening. \n");
       }
     }
   },
 
   callListenerAccept: function tss_callListenerSocket(socketChild) {
     // this method is called at child process when the socket is accepted at parent process.
-    let socket = TCPSocketInternal.createAcceptedChild(socketChild, this._binaryType, this.useGlobal);
+    let socket = new this.useGlobal.mozTCPSocket("", 0, {doNotConnect: true});
+    socket.getInternalSocket().initAcceptedChild(socketChild, this._binaryType, this.useGlobal);
     this._callListenerAcceptCommon(socket);
   },
 
   callListenerError: function tss_callListenerError(message, filename, lineNumber, columnNumber) {
     let init = {
       message: message,
       filename: filename,
       lineno: lineNumber,
@@ -175,18 +176,18 @@ TCPServerSocket.prototype = {
       this._neckoTCPServerSocket.close();
     }
   },
 
   // nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen)
   onSocketAccepted: function tss_onSocketAccepted(server, trans) {
     // precondition: this._inChild == false
     try {
-      let that = TCPSocketInternal.createAcceptedParent(trans, this._binaryType,
-                                                        this.useGlobal);
+      let that = new this.useGlobal.mozTCPSocket("", 0, {doNotConnect: true});
+      that.getInternalSocket().initAcceptedParent(trans, this._binaryType, this.useGlobal);
       this._callListenerAcceptCommon(that);
     }
     catch(e) {
       LOG('exception in onSocketAccepted: ' + e);
       trans.close(Cr.NS_BINDING_ABORTED);
     }
   },
 
--- a/dom/network/TCPServerSocketParent.cpp
+++ b/dom/network/TCPServerSocketParent.cpp
@@ -2,42 +2,25 @@
  * 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 "TCPServerSocketParent.h"
 #include "nsJSUtils.h"
 #include "TCPSocketParent.h"
 #include "mozilla/unused.h"
 #include "mozilla/AppProcessChecker.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "nsGlobalWindow.h"
 #include "nsITCPServerSocketInternal.h"
 
 namespace mozilla {
 namespace dom {
 
-/*static*/ bool
-TCPServerSocketParent::SocketEnabled(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
-{
-  if (!Preferences::GetBool("dom.mozTCPSocket.enabled")) {
-    return false;
-  }
-
-  nsPIDOMWindow* window = xpc::WindowGlobalOrNull(aGlobal);
-  if (!window) {
-    return true;
-  }
-
-  const char* permissions[] = {"tcp-socket", nullptr};
-  return CheckPermissions(aCx, aGlobal, permissions);
-}
-
 static void
 FireInteralError(mozilla::net::PTCPServerSocketParent* aActor,
                  uint32_t aLineNo)
 {
   mozilla::unused <<
       aActor->SendCallbackError(NS_LITERAL_STRING("Internal error"),
                           NS_LITERAL_STRING(__FILE__), aLineNo, 0);
 }
--- a/dom/network/TCPServerSocketParent.h
+++ b/dom/network/TCPServerSocketParent.h
@@ -18,18 +18,16 @@ namespace mozilla {
 namespace dom {
 
 class PBrowserParent;
 
 class TCPServerSocketParent : public mozilla::net::PTCPServerSocketParent
                             , public nsITCPServerSocketParent
 {
 public:
-  static bool SocketEnabled(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
-
   NS_DECL_CYCLE_COLLECTION_CLASS(TCPServerSocketParent)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSITCPSERVERSOCKETPARENT
 
   TCPServerSocketParent();
 
   bool Init(PNeckoParent* neckoParent, const uint16_t& aLocalPort, const uint16_t& aBacklog,
             const nsString& aBinaryType);
--- a/dom/network/TCPSocket.js
+++ b/dom/network/TCPSocket.js
@@ -8,16 +8,20 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 const CC = Components.Constructor;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gSocketTransportService",
+                                   "@mozilla.org/network/socket-transport-service;1",
+                                   "nsISocketTransportService");
+
 const InputStreamPump = CC(
         "@mozilla.org/network/input-stream-pump;1", "nsIInputStreamPump", "init"),
       AsyncStreamCopier = CC(
         "@mozilla.org/network/async-stream-copier;1", "nsIAsyncStreamCopier", "init"),
       ScriptableInputStream = CC(
         "@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", "init"),
       BinaryInputStream = CC(
         "@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", "setInputStream"),
@@ -40,117 +44,54 @@ const BUFFER_SIZE = 65536;
 const NETWORK_STATS_THRESHOLD = 65536;
 
 // XXX we have no TCPError implementation right now because it's really hard to
 // do on b2g18.  On mozilla-central we want a proper TCPError that ideally
 // sub-classes DOMError.  Bug 867872 has been filed to implement this and
 // contains a documented TCPError.webidl that maps all the error codes we use in
 // this file to slightly more readable explanations.
 function createTCPError(aWindow, aErrorName, aErrorType) {
-  return new (aWindow ? aWindow.DOMError : DOMError)(aErrorName);
+  return new DOMError(aErrorName);
 }
 
 
 /*
  * Debug logging function
  */
 
 let debug = false;
 function LOG(msg) {
   if (debug)
     dump("TCPSocket: " + msg + "\n");
 }
 
 /*
- * nsITCPSocketEvent object
- */
-
-function TCPSocketEvent(type, sock, data) {
-  this._type = type;
-  this._target = sock;
-  this._data = data;
-}
-
-// When this API moves to WebIDL and these __exposedProps__ go away, remove
-// this call here and remove the API from XPConnect.
-Cu.skipCOWCallableChecks();
-
-TCPSocketEvent.prototype = {
-  __exposedProps__: {
-    type: 'r',
-    target: 'r',
-    data: 'r',
-    // Promise::ResolveInternal tries to check if the thing being resolved is
-    // itself a promise through the presence of "then".  Accordingly, we list
-    // it as an exposed property, although we return undefined for it.
-    // Bug 882123 covers making TCPSocket be a proper event target with proper
-    // events.
-    then: 'r'
-  },
-  get type() {
-    return this._type;
-  },
-  get target() {
-    return this._target;
-  },
-  get data() {
-    return this._data;
-  },
-  get then() {
-    return undefined;
-  }
-}
-
-/*
  * nsIDOMTCPSocket object
  */
 
 function TCPSocket() {
-  this._readyState = kCLOSED;
+  this.makeGetterSetterEH("onopen");
+  this.makeGetterSetterEH("onclose");
+  this.makeGetterSetterEH("ondrain");
+  this.makeGetterSetterEH("ondata");
+  this.makeGetterSetterEH("onerror");
 
-  this._onopen = null;
-  this._ondrain = null;
-  this._ondata = null;
-  this._onerror = null;
-  this._onclose = null;
+  this._readyState = kCLOSED;
 
   this._binaryType = "string";
 
   this._host = "";
   this._port = 0;
   this._ssl = false;
-
-  this.useWin = null;
 }
 
 TCPSocket.prototype = {
-  __exposedProps__: {
-    open: 'r',
-    host: 'r',
-    port: 'r',
-    ssl: 'r',
-    bufferedAmount: 'r',
-    suspend: 'r',
-    resume: 'r',
-    close: 'r',
-    send: 'r',
-    readyState: 'r',
-    binaryType: 'r',
-    onopen: 'rw',
-    ondrain: 'rw',
-    ondata: 'rw',
-    onerror: 'rw',
-    onclose: 'rw'
-  },
   // The binary type, "string" or "arraybuffer"
   _binaryType: null,
 
-  // Internal
-  _hasPrivileges: null,
-
   // Raw socket streams
   _transport: null,
   _socketInputStream: null,
   _socketOutputStream: null,
 
   // Input stream machinery
   _inputStreamPump: null,
   _inputStreamScriptable: null,
@@ -204,64 +145,47 @@ TCPSocket.prototype = {
     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;
-  },
-  get ondrain() {
-    return this._ondrain;
-  },
-  set ondrain(f) {
-    this._ondrain = f;
-  },
-  get ondata() {
-    return this._ondata;
-  },
-  set ondata(f) {
-    this._ondata = f;
-  },
-  get onerror() {
-    return this._onerror;
-  },
-  set onerror(f) {
-    this._onerror = f;
-  },
-  get onclose() {
-    return this._onclose;
-  },
-  set onclose(f) {
-    this._onclose = f;
-  },
+
+  getEH: function(type) {
+    return this.__DOM_IMPL__.getEventHandler(type);
+   },
+  setEH: function(type, handler) {
+    this.__DOM_IMPL__.setEventHandler(type, handler);
+   },
+  makeGetterSetterEH: function(name) {
+    Object.defineProperty(this, name,
+                          {
+                            get:function()  { return this.getEH(name); },
+                            set:function(h) { return this.setEH(name, h); }
+                          });
+   },
+
 
   _activateTLS: function() {
     let securityInfo = this._transport.securityInfo
           .QueryInterface(Ci.nsISSLSocketControl);
     securityInfo.StartTLS();
   },
 
   // Helper methods.
   _createTransport: function ts_createTransport(host, port, sslMode) {
     let options;
     if (sslMode === 'ssl') {
       options = ['ssl'];
     } else {
       options = ['starttls'];
     }
-    return Cc["@mozilla.org/network/socket-transport-service;1"]
-             .getService(Ci.nsISocketTransportService)
-             .createTransport(options, 1, host, port, null);
+    return gSocketTransportService.createTransport(options, 1, host, port, null);
   },
 
   _sendBufferedAmount: function ts_sendBufferedAmount() {
     if (this._onUpdateBufferedAmount) {
       this._onUpdateBufferedAmount(this.bufferedAmount, this._trackingNumber);
     }
   },
 
@@ -378,20 +302,17 @@ TCPSocket.prototype = {
 
     // Reset the counters once the statistics is saved to NetworkStatsServiceProxy.
     this._txBytes = this._rxBytes = 0;
   },
   // End of helper method for network statistics.
 #endif
 
   callListener: function ts_callListener(type, data) {
-    if (!this["on" + type])
-      return;
-
-    this["on" + type].call(null, new TCPSocketEvent(type, this, data || ""));
+    this.__DOM_IMPL__.dispatchEvent(new this.useWin.TCPSocketEvent(type, {data: data || ""}));
   },
 
   /* nsITCPSocketInternal methods */
   callListenerError: function ts_callListenerError(type, name) {
     // XXX we're not really using TCPError at this time, so there's only a name
     // attribute to pass.
     this.callListener(type, createTCPError(this.useWin, name));
   },
@@ -435,49 +356,16 @@ TCPSocket.prototype = {
         this.callListener("drain");
       }
     } else {
       LOG("bufferedAmount is updated but haven't reaches zero. bufferedAmount: " +
           bufferedAmount);
     }
   },
 
-  createAcceptedParent: function ts_createAcceptedParent(transport, binaryType, windowObject) {
-    let that = new TCPSocket();
-    that._transport = transport;
-    that._initStream(binaryType);
-
-    // ReadyState is kOpen since accepted transport stream has already been connected
-    that._readyState = kOPEN;
-    that._inputStreamPump = new InputStreamPump(that._socketInputStream, -1, -1, 0, 0, false);
-    that._inputStreamPump.asyncRead(that, null);
-
-    // Grab host/port from SocketTransport.
-    that._host = transport.host;
-    that._port = transport.port;
-    that.useWin = windowObject;
-
-    return that;
-  },
-
-  createAcceptedChild: function ts_createAcceptedChild(socketChild, binaryType, windowObject) {
-    let that = new TCPSocket();
-
-    that._binaryType = binaryType;
-    that._inChild = true;
-    that._readyState = kOPEN;
-    socketChild.setSocketAndWindow(that, windowObject);
-    that._socketBridge = socketChild;
-    that._host = socketChild.host;
-    that._port = socketChild.port;
-    that.useWin = windowObject;
-
-    return that;
-  },
-
   setAppId: function ts_setAppId(appId) {
 #ifdef MOZ_WIDGET_GONK
     this._appId = appId;
 #else
     // Do nothing because _appId only exists on Gonk-specific platform.
 #endif
   },
 
@@ -505,46 +393,78 @@ TCPSocket.prototype = {
    */
   onRecvSendFromChild: function(data, byteOffset, byteLength, trackingNumber) {
     this._trackingNumber = trackingNumber;
     this.send(data, byteOffset, byteLength);
   },
 
   /* end nsITCPSocketInternal methods */
 
-  initWindowless: function ts_initWindowless() {
-    try {
-      return Services.prefs.getBoolPref("dom.mozTCPSocket.enabled");
-    } catch (e) {
-      // no pref means return false
-      return false;
+  init: function(aWindow) {
+    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"));
+
+    if (aWindow) {
+      let util = aWindow.QueryInterface(
+        Ci.nsIInterfaceRequestor
+      ).getInterface(Ci.nsIDOMWindowUtils);
+
+      this.useWin = aWindow;
+      this.innerWindowID = util.currentInnerWindowID;
+      LOG("window init: " + this.innerWindowID);
+      Services.obs.addObserver(this, "inner-window-destroyed", true);
     }
   },
 
-  init: function ts_init(aWindow) {
-    if (!this.initWindowless())
-      return null;
+  __init: function(host, port, options) {
+    if (options.doNotConnect) {
+	return;
+    }
 
-    let principal = aWindow.document.nodePrincipal;
-    let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                   .getService(Ci.nsIScriptSecurityManager);
+    LOG("Host info: " + host + ":" + port);
+    this._readyState = kCONNECTING;
+    this._host = host;
+    this._port = port;
+    if (options) {
+      if (options.useSSL) {
+          this._ssl = 'ssl';
+      } else {
+          this._ssl = false;
+      }
+      this._binaryType = options.binaryType || this._binaryType;
+    }
+
+    LOG("SSL: " + this.ssl);
 
-    let perm = principal == secMan.getSystemPrincipal()
-                 ? Ci.nsIPermissionManager.ALLOW_ACTION
-                 : Services.perms.testExactPermissionFromPrincipal(principal, "tcp-socket");
+    if (this._inChild) {
+      this._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"]
+                             .createInstance(Ci.nsITCPSocketChild);
+      this._socketBridge.sendOpen(this, host, port, !!this._ssl,
+                                  this._binaryType, this.useWin, this.useWin || this);
+      return;
+    }
 
-    this._hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
+    let transport = this._transport = this._createTransport(host, port, this._ssl);
+    transport.setEventSink(this, Services.tm.currentThread);
+    this._initStream(this._binaryType);
 
-    let util = aWindow.QueryInterface(
-      Ci.nsIInterfaceRequestor
-    ).getInterface(Ci.nsIDOMWindowUtils);
+#ifdef MOZ_WIDGET_GONK
+    // Set _activeNetwork, which is only required for network statistics.
+    // Note that nsINetworkManager, as well as nsINetworkStatsServiceProxy, is
+    // Gonk-specific.
+    let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
+    if (networkManager) {
+      this._activeNetwork = networkManager.active;
+    }
+#endif
+  },
 
-    this.useWin = XPCNativeWrapper.unwrap(aWindow);
-    this.innerWindowID = util.currentInnerWindowID;
-    LOG("window init: " + this.innerWindowID);
+  initWithGlobal: function(global) {
+    this.useWin = global;
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic == "inner-window-destroyed") {
       let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
       if (wId == this.innerWindowID) {
         LOG("inner-window-destroyed: " + this.innerWindowID);
 
@@ -560,78 +480,43 @@ TCPSocket.prototype = {
         this.useWin = null;
 
         // Clean up our socket
         this.close();
       }
     }
   },
 
-  // nsIDOMTCPSocket
-  open: function ts_open(host, port, options) {
-    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"));
-
-    // 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");
-    LOG("Host info: " + host + ":" + port);
+  initAcceptedParent: function(transport, binaryType, global) {
+    this.useWin = global;
+    this._transport = transport;
+    this._initStream(binaryType);
 
-    that._readyState = kCONNECTING;
-    that._host = host;
-    that._port = port;
-    if (options !== undefined) {
-      if (options.useSecureTransport) {
-          that._ssl = 'ssl';
-      } else {
-          that._ssl = false;
-      }
-      that._binaryType = options.binaryType || that._binaryType;
-    }
-
-    LOG("SSL: " + that.ssl);
+    // ReadyState is kOpen since accepted transport stream has already been connected
+    this._readyState = kOPEN;
+    this._inputStreamPump = new InputStreamPump(this._socketInputStream, -1, -1, 0, 0, false);
+    this._inputStreamPump.asyncRead(this, null);
 
-    if (this._inChild) {
-      that._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"]
-                             .createInstance(Ci.nsITCPSocketChild);
-      that._socketBridge.sendOpen(that, host, port, !!that._ssl,
-                                  that._binaryType, this.useWin, this.useWin || this);
-      return that;
-    }
-
-    let transport = that._transport = this._createTransport(host, port, that._ssl);
-    transport.setEventSink(that, Services.tm.currentThread);
-    that._initStream(that._binaryType);
-
-#ifdef MOZ_WIDGET_GONK
-    // Set _activeNetwork, which is only required for network statistics.
-    // Note that nsINetworkManager, as well as nsINetworkStatsServiceProxy, is
-    // Gonk-specific.
-    let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
-    if (networkManager) {
-      that._activeNetwork = networkManager.active;
-    }
-#endif
-
-    return that;
+    // Grab host/port from SocketTransport.
+    this._host = transport.host;
+    this._port = transport.port;
   },
 
+  initAcceptedChild: function(socketChild, binaryType, global) {
+    this.useWin = global;
+    this._binaryType = binaryType;
+    this._inChild = true;
+    this._readyState = kOPEN;
+    socketChild.setSocketAndWindow(this.getInternalSocket(), this.useWin);
+    this._socketBridge = socketChild;
+    this._host = socketChild.host;
+    this._port = socketChild.port;
+  },
+
+  // nsIDOMTCPSocket
   upgradeToSecure: function ts_upgradeToSecure() {
     if (this._readyState !== kOPEN) {
       throw new Error("Socket not open.");
     }
     if (this._ssl == 'ssl') {
       // Already SSL
       return;
     }
@@ -670,24 +555,25 @@ TCPSocket.prototype = {
 
   send: function ts_send(data, byteOffset, byteLength) {
     if (this._readyState !== kOPEN) {
       throw new Error("Socket not open.");
     }
 
     if (this._binaryType === "arraybuffer") {
       byteLength = byteLength || data.byteLength;
+    } else {
+      byteLength = data.length;
     }
 
     if (this._inChild) {
       this._socketBridge.sendSend(data, byteOffset, byteLength, ++this._trackingNumber);
     }
 
-    let length = this._binaryType === "arraybuffer" ? byteLength : data.length;
-    let newBufferedAmount = this.bufferedAmount + length;
+    let newBufferedAmount = this.bufferedAmount + byteLength;
     let bufferFull = newBufferedAmount >= BUFFER_SIZE;
 
     if (bufferFull) {
       // 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.
@@ -702,17 +588,17 @@ TCPSocket.prototype = {
     }
 
     let new_stream;
     if (this._binaryType === "arraybuffer") {
       new_stream = new ArrayBufferInputStream();
       new_stream.setData(data, byteOffset, byteLength);
     } else {
       new_stream = new StringInputStream();
-      new_stream.setData(data, length);
+      new_stream.setData(data, byteLength);
     }
 
     if (this._waitingForStartTLS) {
       // When we are waiting for starttls, new_stream is added to pendingData
       // and will be appended to multiplexStream after tls had been set up.
       this._pendingDataAfterStartTLS.push(new_stream);
     } else {
       this._multiplexStream.appendStream(new_stream);
@@ -945,44 +831,38 @@ TCPSocket.prototype = {
 
     // We call this even if there is no error.
     this._maybeReportErrorAndCloseIfOpen(status);
   },
 
   // nsIStreamListener (Triggered by _inputStreamPump.asyncRead)
   onDataAvailable: function ts_onDataAvailable(request, context, inputStream, offset, count) {
     if (this._binaryType === "arraybuffer") {
-      let buffer = new (this.useWin ? this.useWin.ArrayBuffer : ArrayBuffer)(count);
+      let buffer = new (this.useWin.ArrayBuffer)(count);
       this._inputStreamBinary.readArrayBuffer(count, buffer);
       this.callListener("data", buffer);
     } else {
       this.callListener("data", this._inputStreamScriptable.read(count));
     }
 
 #ifdef MOZ_WIDGET_GONK
     // Collect received amount for network statistics.
     this._rxBytes += count;
     this._saveNetworkStats(false);
 #endif
   },
 
+  getInternalSocket: function() {
+    return this.QueryInterface(Ci.nsITCPSocketInternal);
+  },
+
   classID: Components.ID("{cda91b22-6472-11e1-aa11-834fec09cd0a}"),
 
-  classInfo: XPCOMUtils.generateCI({
-    classID: Components.ID("{cda91b22-6472-11e1-aa11-834fec09cd0a}"),
-    contractID: "@mozilla.org/tcp-socket;1",
-    classDescription: "Client TCP Socket",
-    interfaces: [
-      Ci.nsIDOMTCPSocket,
-    ],
-    flags: Ci.nsIClassInfo.DOM_OBJECT,
-  }),
-
+  contractID: "@mozilla.org/tcp-socket;1",
   QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIDOMTCPSocket,
     Ci.nsITCPSocketInternal,
     Ci.nsIDOMGlobalPropertyInitializer,
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ])
-}
+};
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TCPSocket]);
--- a/dom/network/TCPSocket.manifest
+++ b/dom/network/TCPSocket.manifest
@@ -1,12 +1,11 @@
 # 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}
 
 # TCPServerSocket.js
 component {73065eae-27dc-11e2-895a-000c29987aa2} TCPServerSocket.js
 contract @mozilla.org/tcp-server-socket;1 {73065eae-27dc-11e2-895a-000c29987aa2}
--- a/dom/network/TCPSocketChild.cpp
+++ b/dom/network/TCPSocketChild.cpp
@@ -4,17 +4,17 @@
 
 #include <algorithm>
 #include "TCPSocketChild.h"
 #include "mozilla/unused.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/TabChild.h"
-#include "nsIDOMTCPSocket.h"
+#include "nsITCPSocketInternal.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jswrapper.h"
 
 using mozilla::net::gNeckoChild;
 
--- a/dom/network/TCPSocketParent.cpp
+++ b/dom/network/TCPSocketParent.cpp
@@ -1,17 +1,17 @@
 /* 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 "nsITCPSocketInternal.h"
 #include "mozilla/unused.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/PNeckoParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/HoldDropJSObjects.h"
@@ -345,22 +345,21 @@ TCPSocketParent::SendEvent(const nsAStri
   }
   mozilla::unused <<
       PTCPSocketParent::SendCallback(nsString(aType), data,
                                      nsString(aReadyState));
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TCPSocketParent::SetSocketAndIntermediary(nsIDOMTCPSocket *socket,
-                                          nsITCPSocketIntermediary *intermediary,
-                                          JSContext* cx)
+TCPSocketParent::SetSocketAndIntermediary(nsITCPSocketInternal *aSocket,
+                                          nsITCPSocketIntermediary *aIntermediary)
 {
-  mSocket = socket;
-  mIntermediary = intermediary;
+  mSocket = aSocket;
+  mIntermediary = aIntermediary;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TCPSocketParent::SendUpdateBufferedAmount(uint32_t aBufferedAmount,
                                           uint32_t aTrackingNumber)
 {
   mozilla::unused << PTCPSocketParent::SendUpdateBufferedAmount(aBufferedAmount,
--- a/dom/network/TCPSocketParent.h
+++ b/dom/network/TCPSocketParent.h
@@ -4,23 +4,24 @@
 
 #ifndef mozilla_dom_TCPSocketParent_h
 #define mozilla_dom_TCPSocketParent_h
 
 #include "mozilla/net/PTCPSocketParent.h"
 #include "nsITCPSocketParent.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
-#include "nsIDOMTCPSocket.h"
 #include "js/TypeDecls.h"
 #include "mozilla/net/OfflineObserver.h"
 
 #define TCPSOCKETPARENT_CID \
   { 0x4e7246c6, 0xa8b3, 0x426d, { 0x9c, 0x17, 0x76, 0xda, 0xb1, 0xe1, 0xe1, 0x4a } }
 
+class nsITCPSocketInternal;
+
 namespace mozilla {
 namespace dom {
 
 class PBrowserParent;
 
 class TCPSocketParentBase : public nsITCPSocketParent
                           , public mozilla::net::DisconnectableParent
 {
@@ -32,17 +33,17 @@ public:
   void ReleaseIPDLReference();
 
 protected:
   TCPSocketParentBase();
   virtual ~TCPSocketParentBase();
 
   JS::Heap<JSObject*> mIntermediaryObj;
   nsCOMPtr<nsITCPSocketIntermediary> mIntermediary;
-  nsCOMPtr<nsIDOMTCPSocket> mSocket;
+  nsCOMPtr<nsITCPSocketInternal> mSocket;
   nsRefPtr<mozilla::net::OfflineObserver> mObserver;
   bool mIPCOpen;
 };
 
 class TCPSocketParent : public mozilla::net::PTCPSocketParent
                       , public TCPSocketParentBase
 {
 public:
--- a/dom/network/TCPSocketParentIntermediary.js
+++ b/dom/network/TCPSocketParentIntermediary.js
@@ -14,17 +14,17 @@ Cu.import("resource://gre/modules/Servic
 let global = this;
 
 function TCPSocketParentIntermediary() {
 }
 
 TCPSocketParentIntermediary.prototype = {
   _setCallbacks: function(aParentSide, socket) {
     aParentSide.initJS(this);
-    this._socket = socket;
+    this._socket = socket.getInternalSocket();
 
     // Create handlers for every possible callback that attempt to trigger
     // corresponding callbacks on the child object.
     // ondrain event is not forwarded, since the decision of firing ondrain
     // is made in child.
     ["open", "data", "error", "close"].forEach(
       function(p) {
         socket["on" + p] = function(data) {
@@ -36,61 +36,59 @@ TCPSocketParentIntermediary.prototype = 
   },
 
   _onUpdateBufferedAmountHandler: function(aParentSide, aBufferedAmount, aTrackingNumber) {
     aParentSide.sendUpdateBufferedAmount(aBufferedAmount, aTrackingNumber);
   },
 
   open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType,
                  aAppId, aInBrowser) {
-    let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
-    let socket = baseSocket.open(aHost, aPort, {useSecureTransport: aUseSSL, binaryType: aBinaryType});
-    if (!socket)
-      return null;
+    let socket = new global.mozTCPSocket(aHost, aPort, {useSecureTransport: aUseSSL, binaryType: aBinaryType});
 
-    let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal);
+    let socketInternal = socket.getInternalSocket();
+    socketInternal.initWithGlobal(global);
     socketInternal.setAppId(aAppId);
     socketInternal.setInBrowser(aInBrowser);
 
     // Handle parent's request to update buffered amount.
     socketInternal.setOnUpdateBufferedAmountHandler(
       this._onUpdateBufferedAmountHandler.bind(this, aParentSide));
 
     // Handlers are set to the JS-implemented socket object on the parent side.
     this._setCallbacks(aParentSide, socket);
-    return socket;
+    return socketInternal;
   },
 
   listen: function(aTCPServerSocketParent, aLocalPort, aBacklog, aBinaryType,
                    aAppId, aInBrowser) {
     let serverSocket = new global.mozTCPServerSocket(aLocalPort, { binaryType: aBinaryType }, aBacklog);
     let serverSocketInternal = serverSocket.getInternalSocket();
     serverSocketInternal.initWithGlobal(global);
 
     let localPort = serverSocket.localPort;
 
     serverSocket["onconnect"] = function(event) {
       var socketParent = Cc["@mozilla.org/tcp-socket-parent;1"]
                             .createInstance(Ci.nsITCPSocketParent);
       var intermediary = new TCPSocketParentIntermediary();
 
-      let socketInternal = event.socket.QueryInterface(Ci.nsITCPSocketInternal);
+      let socketInternal = event.socket.getInternalSocket();
       socketInternal.setAppId(aAppId);
       socketInternal.setInBrowser(aInBrowser);
       socketInternal.setOnUpdateBufferedAmountHandler(
         intermediary._onUpdateBufferedAmountHandler.bind(intermediary, socketParent));
 
       // Handlers are set to the JS-implemented socket object on the parent side,
       // so that the socket parent object can communicate data
       // with the corresponding socket child object through IPC.
       intermediary._setCallbacks(socketParent, event.socket);
       // The members in the socket parent object are set with arguments,
       // so that the socket parent object can communicate data
       // with the JS socket object on the parent side via the intermediary object.
-      socketParent.setSocketAndIntermediary(event.socket, intermediary);
+      socketParent.setSocketAndIntermediary(socketInternal, intermediary);
       aTCPServerSocketParent.sendCallbackAccept(socketParent);
     };
 
     serverSocket["onerror"] = function(data) {
         var error = data.data;
 
         aTCPServerSocketParent.sendCallbackError(error.message, error.filename,
                                                  error.lineNumber, error.columnNumber);
new file mode 100644
--- /dev/null
+++ b/dom/network/TCPSocketUtils.cpp
@@ -0,0 +1,31 @@
+/* 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 "TCPSocketUtils.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsGlobalWindow.h"
+
+namespace TCPSocketUtils {
+
+using mozilla::Preferences;
+using mozilla::dom::CheckPermissions;
+
+bool
+SocketEnabled(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
+{
+  if (!Preferences::GetBool("dom.mozTCPSocket.enabled")) {
+    return false;
+  }
+
+  nsPIDOMWindow* window = xpc::WindowGlobalOrNull(aGlobal);
+  if (!window) {
+    return true;
+  }
+
+  const char* permissions[] = {"tcp-socket", nullptr};
+  return CheckPermissions(aCx, aGlobal, permissions);
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/dom/network/TCPSocketUtils.h
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TCPSocketUtils_h
+#define TCPSocketUtils_h
+
+#include "js/RootingAPI.h"
+
+namespace TCPSocketUtils {
+
+extern bool
+SocketEnabled(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+
+}
+
+#endif // TCPSocketUtils_h
--- a/dom/network/interfaces/moz.build
+++ b/dom/network/interfaces/moz.build
@@ -1,21 +1,21 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
-    'nsIDOMTCPSocket.idl',
     'nsIMozNavigatorNetwork.idl',
     'nsITCPServerSocketChild.idl',
     'nsITCPServerSocketInternal.idl',
     'nsITCPServerSocketParent.idl',
     'nsITCPSocketChild.idl',
+    'nsITCPSocketInternal.idl',
     'nsITCPSocketParent.idl',
     'nsIUDPSocketChild.idl',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     XPIDL_SOURCES += [
         'nsIDOMNetworkStatsManager.idl',
         'nsINetworkStatsServiceProxy.idl',
deleted file mode 100644
--- a/dom/network/interfaces/nsIDOMTCPSocket.idl
+++ /dev/null
@@ -1,312 +0,0 @@
-/* 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/. */
-
-/**
- * MozTCPSocket exposes a TCP client and server sockets
- * to highly privileged apps. It provides a buffered, non-blocking
- * interface for sending. For receiving, it uses an asynchronous,
- * event handler based interface.
- */
-
-#include "domstubs.idl"
-#include "nsIDOMEvent.idl"
-#include "nsITCPSocketChild.idl"
-
-interface nsISocketTransport;
-
-// Bug 731746 - Allow chrome JS object to implement nsIDOMEventTarget
-// nsITCPSocket should be an nsIEventTarget but js objects
-// cannot be an nsIEventTarget yet
-// #include "nsIEventTarget.idl"
-
-// Bug 723206 - Constructors implemented in JS from IDL should be
-//              allowed to have arguments
-//
-//  Once bug 723206 will be fixed, this method could be replaced by
-//  arguments when instantiating a TCPSocket object. For example it will
-//  be possible to do (similarly to the WebSocket API):
-//    var s = new MozTCPSocket(host, port);
-
-// Bug 797561 - Expose a server tcp socket API to web applications
-
-
-[scriptable, uuid(8D7BF931-CBBF-4D7C-98F6-9B1F1359E659)]
-interface nsIDOMTCPSocket : nsISupports
-{
-  /**
-   * Create and return a socket object which will attempt to connect to
-   * the given host and port.
-   *
-   * @param host The hostname of the server to connect to.
-   * @param port The port to connect to.
-   * @param options An object specifying one or more parameters which
-   *                determine the details of the socket.
-   *
-   *        useSecureTransport: true to create an SSL socket. Defaults to false.
-   *
-   *        binaryType: "arraybuffer" to use ArrayBuffer
-   *          instances in the ondata callback and as the argument
-   *          to send. Defaults to "string", to use JavaScript strings.
-   *
-   * @return The new TCPSocket instance.
-   */
-  nsIDOMTCPSocket open(in DOMString host, in unsigned short port, [optional] in jsval options);
-
-  /**
-   * Enable secure on channel.
-   */
-  void upgradeToSecure();
-
-  /**
-   * The host of this socket object.
-   */
-  readonly attribute DOMString host;
-
-  /**
-   * The port of this socket object.
-   */
-  readonly attribute unsigned short port;
-
-  /**
-   * True if this socket object is an SSL socket.
-   */
-  readonly attribute boolean ssl;
-
-  /**
-   * The number of bytes which have previously been buffered by calls to
-   * send on this socket.
-   */
-  readonly attribute unsigned long bufferedAmount;
-
-  /**
-   * Pause reading incoming data and invocations of the ondata handler until
-   * resume is called.
-   */
-  void suspend();
-
-  /**
-   * Resume reading incoming data and invoking ondata as usual.
-   */
-  void resume();
-
-  /**
-   * Close the socket.
-   */
-  void close();
-
-  /**
-   * Write data to the socket.
-   *
-   * @param data The data to write to the socket. If
-   *             binaryType: "arraybuffer" was passed in the options
-   *             object, then this object should be an ArrayBuffer instance.
-   *             If binaryType: "string" was passed, or if no binaryType
-   *             option was specified, then this object should be an
-   *             ordinary JavaScript string.
-   * @param byteOffset The offset within the data from which to begin writing.
-   *                   Has no effect on non-ArrayBuffer data.
-   * @param byteLength The number of bytes to write. Has no effect on
-   *                   non-ArrayBuffer data.
-   *
-   * @return Send returns true or false as a hint to the caller that
-   *         they may either continue sending more data immediately, or
-   *         may want to wait until the other side has read some of the
-   *         data which has already been written to the socket before
-   *         buffering more. If send returns true, then less than 64k
-   *         has been buffered and it's safe to immediately write more.
-   *         If send returns false, then more than 64k has been buffered,
-   *         and the caller may wish to wait until the ondrain event
-   *         handler has been called before buffering more data by more
-   *         calls to send.
-   */
-  boolean send(in jsval data, [optional] in unsigned long byteOffset, [optional] in unsigned long byteLength);
-
-  /**
-   * The readyState attribute indicates which state the socket is currently
-   * in. The state will be either "connecting", "open", "closing", or "closed".
-   */
-  readonly attribute DOMString readyState;
-
-  /**
-   * The binaryType attribute indicates which mode this socket uses for
-   * sending and receiving data. If the binaryType: "arraybuffer" option
-   * was passed to the open method that created this socket, binaryType
-   * will be "arraybuffer". Otherwise, it will be "string".
-   */
-  readonly attribute DOMString binaryType;
-
-  /**
-   * The onopen event handler is called when the connection to the server
-   * has been established. If the connection is refused, onerror will be
-   * called, instead.
-   */
-  attribute jsval onopen;
-
-  /**
-   * After send has buffered more than 64k of data, it returns false to
-   * indicate that the client should pause before sending more data, to
-   * avoid accumulating large buffers. This is only advisory, and the client
-   * is free to ignore it and buffer as much data as desired, but if reducing
-   * the size of buffers is important (especially for a streaming application)
-   * ondrain will be called once the previously-buffered data has been written
-   * to the network, at which point the client can resume calling send again.
-   */
-  attribute jsval ondrain;
-
-  /**
-   * The ondata handler will be called repeatedly and asynchronously after
-   * onopen has been called, every time some data was available from the server
-   * and was read. If binaryType: "arraybuffer" was passed to open, the data
-   * attribute of the event object will be an ArrayBuffer. If not, it will be a
-   * normal JavaScript string.
-   *
-   * At any time, the client may choose to pause reading and receiving ondata
-   * callbacks, by calling the socket's suspend() method. Further invocations
-   * of ondata will be paused until resume() is called.
-   */
-  attribute jsval ondata;
-
-  /**
-   * The onerror handler will be called when there is an error. The data
-   * attribute of the event passed to the onerror handler will have a
-   * description of the kind of error.
-   *
-   * If onerror is called before onopen, the error was connection refused,
-   * and onclose will not be called. If onerror is called after onopen,
-   * the connection was lost, and onclose will be called after onerror.
-   */
-  attribute jsval onerror;
-
-  /**
-   * The onclose handler is called once the underlying network socket
-   * has been closed, either by the server, or by the client calling
-   * close.
-   *
-   * If onerror was not called before onclose, then either side cleanly
-   * closed the connection.
-   */
-  attribute jsval onclose;
-};
-
-/*
- * This interface is implemented in TCPSocket.js as an 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(ac2c4b69-cb79-4767-b1ce-bcf62945cd39)]
-interface nsITCPSocketInternal : nsISupports {
-  // Trigger the callback for |type| and provide a DOMError() object with the given data
-  void callListenerError(in DOMString type, in DOMString name);
-
-  // 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 an ArrayBuffer 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.
-  // @param readyState
-  //        new ready state to be set to TCPSocket.
-  void updateReadyState(in DOMString readyState);
-
-  // Update the DOM object's bufferedAmount value with a tracking number to
-  // ensure the update request is sent after child's send() invocation.
-  // @param bufferedAmount
-  //        TCPSocket parent's bufferedAmount.
-  // @param trackingNumber
-  //        A number to ensure the bufferedAmount is updated after data
-  //        from child are sent to parent.
-  void updateBufferedAmount(in uint32_t bufferedAmount,
-                            in uint32_t trackingNumber);
-
-  // Create a socket object on the parent side.
-  // This is called in accepting any open request on the parent side.
-  //
-  // @param transport
-  //        The accepted socket transport.
-  // @param binaryType
-  //        "arraybuffer" to use ArrayBuffer instances
-  //        in the ondata callback and as the argument to send.
-  // @param global
-  //        An object to create ArrayBuffer for this global. See Bug 831107.
-  nsIDOMTCPSocket createAcceptedParent(in nsISocketTransport transport,
-                                       in DOMString binaryType,
-                                       in nsISupports global);
-
-  // Create a DOM socket on the child side
-  // This is called when the socket is accepted on the parent side.
-  //
-  // @param socketChild
-  //        The socket child object for the IPC implementation.
-  // @param binaryType
-  //        "arraybuffer" to use ArrayBuffer instances
-  //        in the ondata callback and as the argument to send.
-  // @param window
-  //        An object to create ArrayBuffer for this window. See Bug 831107.
-  nsIDOMTCPSocket createAcceptedChild(in nsITCPSocketChild socketChild,
-                                      in DOMString binaryType,
-                                      in nsIDOMWindow window);
-
-  // Set App ID.
-  void setAppId(in unsigned long appId);
-
-  // Set inBrowser.
-  void setInBrowser(in boolean inBrowser);
-
-  // Set a callback that handles the request from a TCP socket parent when that
-  // socket parent wants to notify that its bufferedAmount is updated.
-  void setOnUpdateBufferedAmountHandler(in jsval handler);
-
-  // Providing child process with ability to pass more arguments to parent's
-  // send() function.
-  // @param trackingNumber
-  //        To ensure the request to update bufferedAmount in child is after
-  //        lastest send() invocation from child.
-  void onRecvSendFromChild(in jsval data, in unsigned long byteOffset,
-                           in unsigned long byteLength, in unsigned long trackingNumber);
-};
-
-/**
- * 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)]
-interface nsITCPSocketEvent : nsISupports {
-  /**
-   * The socket object which produced this event.
-   */
-  readonly attribute nsIDOMTCPSocket target;
-
-  /**
-   * The type of this event. One of:
-   *
-   * open
-   * error
-   * data
-   * drain
-   * close
-   */
-  readonly attribute DOMString type;
-
-  /**
-   * The data related to this event, if any. In the ondata callback,
-   * data will be the bytes read from the network; if the binaryType
-   * of the socket was "arraybuffer", this value will be of type ArrayBuffer;
-   * otherwise, it will be a normal JavaScript string.
-   *
-   * In the onerror callback, data will be a string with a description
-   * of the error.
-   *
-   * In the other callbacks, data will be an empty string.
-   */
-  readonly attribute jsval data;
-};
-
--- a/dom/network/interfaces/nsITCPSocketParent.idl
+++ b/dom/network/interfaces/nsITCPSocketParent.idl
@@ -1,15 +1,15 @@
 /* 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 nsITCPSocketInternal;
 interface nsITCPServerSocketInternal;
 interface nsITCPServerSocketParent;
 interface nsITCPSocketIntermediary;
 
 // Interface required to allow the TCP socket object (TCPSocket.js) in the
 // parent process to talk to the parent IPC actor, TCPSocketParent, which
 // is written in C++.
 [scriptable, uuid(03B864C8-AAA9-4062-943E-AE6397070E0D)]
@@ -35,18 +35,18 @@ interface nsITCPSocketParent : nsISuppor
   // Initialize a parent socket object. It is called from the parent side socket,
   // which is generated in accepting any open request on the parent side.
   // The socket after being initialized will be established.
   //
   // @param socket
   //        The socket on the parent side.
   // @param intermediary
   //        Intermediate class object. See nsITCPSocketIntermediary.
-  [implicit_jscontext] void setSocketAndIntermediary(in nsIDOMTCPSocket socket,
-                                                     in nsITCPSocketIntermediary intermediary);
+  void setSocketAndIntermediary(in nsITCPSocketInternal socket,
+                                in nsITCPSocketIntermediary intermediary);
 
   // When parent's buffered amount is updated and it wants to inform child to
   // update the bufferedAmount as well.
   //
   // @param bufferedAmount
   //        The new value of bufferedAmount that is going to be set to child's
   //        bufferedAmount.
   // @param trackingNumber
@@ -63,21 +63,21 @@ interface nsITCPSocketParent : nsISuppor
 // Intermediate class to handle sending multiple possible data types
 // and kicking off the chrome process socket object's connection.
 // This interface is the bridge of TCPSocketParent, which is written in C++,
 // and TCPSocket, which is written in Javascript. TCPSocketParentIntermediary
 // implements nsITCPSocketIntermediary in Javascript.
 [scriptable, uuid(aa9bd46d-26bf-4ba8-9c18-ba02482c02f0)]
 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,
-                       in unsigned long appId,
-                       in boolean inBrowser);
+  nsITCPSocketInternal open(in nsITCPSocketParent parent,
+                            in DOMString host, in unsigned short port,
+                            in boolean useSSL, in DOMString binaryType,
+                            in unsigned long appId,
+                            in boolean inBrowser);
 
   // Listen on a port
   nsITCPServerSocketInternal listen(in nsITCPServerSocketParent parent,
                                     in unsigned short port, in unsigned short backlog,
                                     in DOMString binaryType,
                                     in unsigned long appId,
                                     in boolean inBrowser);
 
--- a/dom/network/moz.build
+++ b/dom/network/moz.build
@@ -12,17 +12,17 @@ XPCSHELL_TESTS_MANIFESTS += [
 ]
 
 if CONFIG['MOZ_B2G_RIL']:
     XPCSHELL_TESTS_MANIFESTS += ['tests/unit_stats/xpcshell.ini']
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 
 EXPORTS += [
-    'TCPServerSocketParent.h',
+    'TCPSocketUtils.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'UDPSocket.h',
 ]
 
 EXPORTS.mozilla.dom.network += [
     'Connection.h',
@@ -37,16 +37,17 @@ EXPORTS.mozilla.dom.network += [
 ]
 
 UNIFIED_SOURCES += [
     'Connection.cpp',
     'TCPServerSocketChild.cpp',
     'TCPServerSocketParent.cpp',
     'TCPSocketChild.cpp',
     'TCPSocketParent.cpp',
+    'TCPSocketUtils.cpp',
     'UDPSocket.cpp',
     'UDPSocketChild.cpp',
     'UDPSocketParent.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     EXTRA_JS_MODULES += [
         'NetworkStatsDB.jsm',
--- a/dom/network/tests/mochitest.ini
+++ b/dom/network/tests/mochitest.ini
@@ -4,18 +4,16 @@ support-files =
   file_udpsocket_iframe.html
   test_tcpsocket_client_and_server_basics.js
 
 [test_network_basics.html]
 skip-if = toolkit == "gonk" || toolkit == 'android'
 [test_tcpsocket_client_and_server_basics.html]
 [test_tcpsocket_default_permissions.html]
 skip-if = toolkit == "gonk"
-[test_tcpsocket_enabled_no_perm.html]
-skip-if = toolkit == "gonk"
 [test_tcpsocket_enabled_with_perm.html]
 skip-if = toolkit == "gonk" || e10s
 [test_networkstats_alarms.html]
 skip-if = true # Bug 958689
 [test_networkstats_basics.html]
 skip-if = true # Bug 958689, bug 858005
 [test_networkstats_disabled.html]
 skip-if = toolkit != "gonk"
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -170,27 +170,26 @@ function* test_basics() {
     permDeferred.resolve);
   yield permDeferred.promise;
 
   // See bug 903830; in e10s mode we never get to find out the localPort if we
   // let it pick a free port by choosing 0.  This is the same port the xpcshell
   // test was using.
   let serverPort = 8085;
 
-  let TCPSocket = navigator.mozTCPSocket;
   // - Start up a listening socket.
   let listeningServer = new mozTCPServerSocket(serverPort,
                                                { binaryType: 'arraybuffer' },
                                                SERVER_BACKLOG);
 
   let connectedPromise = waitForConnection(listeningServer);
 
   // -- Open a connection to the server
-  let clientSocket = TCPSocket.open('127.0.0.1', serverPort,
-                                    { binaryType: 'arraybuffer' });
+  let clientSocket = new mozTCPSocket('127.0.0.1', serverPort,
+                                      { binaryType: 'arraybuffer' });
   let clientQueue = listenForEventsOnSocket(clientSocket, 'client');
 
   // (the client connects)
   is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
   is(clientSocket.readyState, 'open', 'client readyState is open');
 
   // (the server connected)
   let { socket: serverSocket, queue: serverQueue } = yield connectedPromise;
@@ -286,18 +285,18 @@ function* test_basics() {
      'client readyState should be closed after close event');
   is((yield serverQueue.waitForEvent()).type, 'close',
      'The server should get a close event when it closes itself.');
   is(serverSocket.readyState, 'closed',
      'server readyState should be closed after close event');
 
   // -- Re-establish connection
   connectedPromise = waitForConnection(listeningServer);
-  clientSocket = TCPSocket.open('127.0.0.1', serverPort,
-                                { binaryType: 'arraybuffer' });
+  clientSocket = new mozTCPSocket('127.0.0.1', serverPort,
+                                  { binaryType: 'arraybuffer' });
   clientQueue = listenForEventsOnSocket(clientSocket, 'client');
   is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
 
   let connectedResult = yield connectedPromise;
   // destructuring assignment is not yet ES6 compliant, must manually unpack
   serverSocket = connectedResult.socket;
   serverQueue = connectedResult.queue;
 
@@ -313,18 +312,18 @@ function* test_basics() {
   is((yield serverQueue.waitForEvent()).type, 'close',
      'The server should get a close event when the client closes.');
   is(serverSocket.readyState, 'closed',
      'server readyState should be closed after the close event is received');
 
 
   // -- Re-establish connection
   connectedPromise = waitForConnection(listeningServer);
-  clientSocket = TCPSocket.open('127.0.0.1', serverPort,
-                                { binaryType: 'arraybuffer' });
+  clientSocket = new mozTCPSocket('127.0.0.1', serverPort,
+                                  { binaryType: 'arraybuffer' });
   clientQueue = listenForEventsOnSocket(clientSocket, 'client');
   is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
 
   connectedResult = yield connectedPromise;
   // destructuring assignment is not yet ES6 compliant, must manually unpack
   serverSocket = connectedResult.socket;
   serverQueue = connectedResult.queue;
 
@@ -351,17 +350,17 @@ function* test_basics() {
 
 
   // -- Close the listening server (and try to connect)
   // We want to verify that the server actually closes / stops listening when
   // we tell it to.
   listeningServer.close();
 
   // - try and connect, get an error
-  clientSocket = TCPSocket.open('127.0.0.1', serverPort,
-                                { binaryType: 'arraybuffer' });
+  clientSocket = new mozTCPSocket('127.0.0.1', serverPort,
+                                  { binaryType: 'arraybuffer' });
   clientQueue = listenForEventsOnSocket(clientSocket, 'client');
   is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect');
   is(clientSocket.readyState, 'closed',
      'client readyState should be closed after the failure to connect');
 }
 
 add_task(test_basics);
--- a/dom/network/tests/test_tcpsocket_default_permissions.html
+++ b/dom/network/tests/test_tcpsocket_default_permissions.html
@@ -9,19 +9,14 @@
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test to ensure TCPSocket permission is disabled by default **/
 
-try {
-  navigator.mozTCPSocket;
-  throw new Error("Error: navigator.mozTCPSocket should not exist by default");
-} catch (e) {
-  ok(true, "navigator.mozTCPSocket should not exist by default");  
-}
+ok(!("mozTCPSocket" in window), "mozTCPSocket should not exist by default");
 
 </script>
 </pre>
 </body>
 </html>
deleted file mode 100644
--- a/dom/network/tests/test_tcpsocket_enabled_no_perm.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test to ensure TCPSocket permission enabled and no tcp-socket perm does not allow open</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test to ensure TCPSocket permission being turned on enables 
-  navigator.mozTCPSocket, but mozTCPSocket.open does not work
-  in content.
-**/
-SpecialPowers.setBoolPref("dom.mozTCPSocket.enabled", true);
-
-ok('mozTCPSocket' in navigator, "navigator.mozTCPSocket should be accessible if dom.mozTCPSocket.enabled is true");
-
-try {
-  navigator.mozTCPSocket.open('localhost', 80);
-  throw new Error("Error: navigator.mozTCPSocket.open should raise for content that does not have the tcp-socket permission");
-} catch (e) {
-  ok(true, "navigator.mozTCPSocket.open should raise for content that does not have the tcp-socket permission");
-}
-
-SpecialPowers.setBoolPref("dom.mozTCPSocket.enabled", false);
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/network/tests/test_tcpsocket_enabled_with_perm.html
+++ b/dom/network/tests/test_tcpsocket_enabled_with_perm.html
@@ -7,25 +7,25 @@
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test to ensure TCPSocket permission being turned on enables 
-  navigator.mozTCPSocket, and mozTCPSocket.open works when
+/** Test to ensure TCPSocket permission being turned on enables
+  mozTCPSocket, and mozTCPSocket constructor works when
   the tcp-socket permission has been granted.
 **/
 SpecialPowers.setBoolPref("dom.mozTCPSocket.enabled", true);
 SpecialPowers.addPermission("tcp-socket", true, document);
 
-ok('mozTCPSocket' in navigator, "navigator.mozTCPSocket should be accessible if dom.mozTCPSocket.enabled is true");
+ok('mozTCPSocket' in window, "window.mozTCPSocket should be accessible if dom.mozTCPSocket.enabled is true");
 
-ok(navigator.mozTCPSocket.open('localhost', 80), "navigator.mozTCPSocket.open should work for content that has the tcp-socket permission");
+ok(new mozTCPSocket('localhost', 80), "window.mozTCPSocket ctor should work for content that has the tcp-socket permission");
 
 SpecialPowers.setBoolPref("dom.mozTCPSocket.enabled", false);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/network/tests/unit/test_tcpsocket.js
+++ b/dom/network/tests/unit/test_tcpsocket.js
@@ -56,17 +56,17 @@ const ServerSocket = CC("@mozilla.org/ne
                            "init"),
       BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
                              "nsIBinaryInputStream",
                              "setInputStream"),
       BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
                               "nsIBinaryOutputStream",
                               "setOutputStream"),
       TCPSocket = new (CC("@mozilla.org/tcp-socket;1",
-                     "nsIDOMTCPSocket"))();
+                     "nsITCPSocketInternal"))();
 
 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");
 
 /**
  *
@@ -264,19 +264,20 @@ var server = null, sock = null, failure_
  * test, and is also added after every test which results in the socket
  * being closed.
  */
 
 function connectSock() {
   server.reset();
   var yayFuncs = makeJointSuccess(['serveropen', 'clientopen']);
 
-  sock = TCPSocket.open(
+  sock = new mozTCPSocket(
     '127.0.0.1', server.listener.port,
     { binaryType: 'arraybuffer' });
+  sock.getInternalSocket().initWithGlobal(this);
 
   sock.onopen = yayFuncs.clientopen;
   sock.ondrain = null;
   sock.ondata = makeFailureCase('data');
   sock.onerror = makeFailureCase('error');
   sock.onclose = makeFailureCase('close');
 
   server.onconnect = yayFuncs.serveropen;
@@ -305,17 +306,17 @@ function childbuffered() {
   sock.ondrain = function() {
     yays.ondrain();
     sock.close();
   };
 
   server.ondata = makeExpectData(
     'ondata', DATA_ARRAY, false, yays.serverdata);
 
-  let internalSocket = sock.QueryInterface(Ci.nsITCPSocketInternal);
+  let internalSocket = sock.getInternalSocket();
   internalSocket.updateBufferedAmount(65535, // almost reach buffering threshold
                                       0);
   if (sock.send(DATA_ARRAY_BUFFER)) {
     do_throw("expected sock.send to return false.");
   }
 
   sock.onclose = yays.clientclose;
   server.onclose = yays.serverclose;
@@ -330,17 +331,17 @@ function childbuffered() {
 // 3. wait for 1 second, to make sure there's no ondrain event dispatched in
 //    child.
 function childnotbuffered() {
   let yays = makeJointSuccess(['serverdata', 'clientclose', 'serverclose']);
   server.ondata = makeExpectData('ondata', BIG_ARRAY, false, yays.serverdata);
   if (sock.send(BIG_ARRAY_BUFFER)) {
     do_throw("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering");
   }
-  let internalSocket = sock.QueryInterface(Ci.nsITCPSocketInternal);
+  let internalSocket = sock.getInternalSocket();
   internalSocket.updateBufferedAmount(0, // setting zero will clear waitForDrain in sock.
                                       1);
 
   // shouldn't get ondrain, even after parent have cleared its buffer.
   sock.ondrain = makeFailureCase('drain');
   sock.onclose = yays.clientclose;
   server.onclose = yays.serverclose;
   do_timeout(1000, function() {
--- a/dom/network/tests/unit_ipc/test_tcpsocket_ipc.js
+++ b/dom/network/tests/unit_ipc/test_tcpsocket_ipc.js
@@ -1,9 +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
+}
--- a/dom/permission/tests/test_tcp-socket.html
+++ b/dom/permission/tests/test_tcp-socket.html
@@ -16,34 +16,28 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script type="application/javascript;version=1.8" src="file_framework.js"></script>
 <script type="application/javascript;version=1.8">
 /* mozTCPSocket only returns null on window init
  * if the permission isn't set
  */
 function verifier(success, failure) {
   try {
-    var conn = this.getObj().open("http://mochi.test/", 80);
-
-    if (conn) {
-      success("Opened connection");
-    } else {
-      failure("failed to open connection");
-    }
+    var conn = new mozTCPSocket("http://mochi.test/", 80);
+    success("Opened connection");
   } catch (e) {
     failure("Got an exception " + e);
   }
 }
 
 var gData = [
   {
     perm: ["tcp-socket"],
     needParentPerm: true,
-    obj: "mozTCPSocket",
-    idl: "nsIDOMTCPSocket",
+    webidl: "mozTCPSocket",
     settings: [["dom.mozTCPSocket.enabled", true]],
     verifier: verifier.toSource(),
   }
 ]
 </script>
 </pre>
 </body>
 </html>
--- a/dom/webidl/TCPServerSocket.webidl
+++ b/dom/webidl/TCPServerSocket.webidl
@@ -16,17 +16,17 @@ enum TCPServerSocketBinaryType {
   "string"
 };
 
 dictionary ServerSocketOptions {
   TCPServerSocketBinaryType binaryType = "string";
 };
 
 [Constructor(unsigned short port, optional ServerSocketOptions options, optional unsigned short backlog),
- JSImplementation="@mozilla.org/tcp-server-socket;1", Func="TCPServerSocketParent::SocketEnabled",
+ JSImplementation="@mozilla.org/tcp-server-socket;1", Func="TCPSocketUtils::SocketEnabled",
  Exposed=(Window,System)]
 interface mozTCPServerSocket : EventTarget {
   /**
    * The port of this server socket object.
    */
   readonly attribute unsigned short localPort;
 
   /**
--- a/dom/webidl/TCPServerSocketEvent.webidl
+++ b/dom/webidl/TCPServerSocketEvent.webidl
@@ -1,15 +1,15 @@
 /* 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/. */
 
 interface nsIDOMTCPSocket;
 
 [Constructor(DOMString type, optional TCPServerSocketEventInit eventInitDict),
- Func="TCPServerSocketParent::SocketEnabled", Exposed=(Window,System)]
+ Func="TCPSocketUtils::SocketEnabled", Exposed=(Window,System)]
 interface TCPServerSocketEvent : Event {
-  readonly attribute nsIDOMTCPSocket socket; // mozTCPSocket
+  readonly attribute mozTCPSocket socket;
 };
 
 dictionary TCPServerSocketEventInit : EventInit {
-  nsIDOMTCPSocket? socket = null;
+  mozTCPSocket? socket = null;
 };
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TCPSocket.webidl
@@ -0,0 +1,162 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+interface nsITCPSocketInternal;
+
+/**
+ * MozTCPSocket exposes a TCP client socket (no server sockets yet)
+ * to highly privileged apps. It provides a buffered, non-blocking
+ * interface for sending. For receiving, it uses an asynchronous,
+ * event handler based interface.
+ */
+
+enum TCPSocketBinaryType {
+  "arraybuffer",
+  "string"
+};
+
+dictionary SocketOptions {
+  boolean useSSL = false;
+  TCPSocketBinaryType binaryType = "string";
+  boolean doNotConnect = false;
+};
+
+[Constructor(DOMString host, unsigned short port, optional SocketOptions options),
+ Func="TCPSocketUtils::SocketEnabled",
+ JSImplementation="@mozilla.org/tcp-socket;1", Exposed=(Window,System)]
+interface mozTCPSocket : EventTarget {
+  /**
+   * The host of this socket object.
+   */
+  readonly attribute USVString host;
+
+  /**
+   * The port of this socket object.
+   */
+  readonly attribute unsigned short port;
+
+  /**
+   * True if this socket object is an SSL socket.
+   */
+  readonly attribute boolean ssl;
+
+  /**
+   * The number of bytes which have previously been buffered by calls to
+   * send on this socket.
+   */
+  readonly attribute unsigned long bufferedAmount;
+
+  /**
+   * Pause reading incoming data and invocations of the ondata handler until
+   * resume is called.
+   */
+  void suspend();
+
+  /**
+   * Resume reading incoming data and invoking ondata as usual.
+   */
+  void resume();
+
+  /**
+   * Close the socket.
+   */
+  void close();
+
+  /**
+   * Write data to the socket.
+   *
+   * @param data The data to write to the socket. If
+   *             binaryType: "arraybuffer" was passed in the options
+   *             object, then this object should be an ArrayBuffer instance.
+   *             If binaryType: "string" was passed, or if no binaryType
+   *             option was specified, then this object should be an
+   *             ordinary JavaScript string.
+   * @param byteOffset The offset within the data from which to begin writing.
+   *                   Has no effect on non-ArrayBuffer data.
+   * @param byteLength The number of bytes to write. Has no effect on
+   *                   non-ArrayBuffer data.
+   *
+   * @return Send returns true or false as a hint to the caller that
+   *         they may either continue sending more data immediately, or
+   *         may want to wait until the other side has read some of the
+   *         data which has already been written to the socket before
+   *         buffering more. If send returns true, then less than 64k
+   *         has been buffered and it's safe to immediately write more.
+   *         If send returns false, then more than 64k has been buffered,
+   *         and the caller may wish to wait until the ondrain event
+   *         handler has been called before buffering more data by more
+   *         calls to send.
+   */
+  boolean send((USVString or ArrayBuffer) data, optional unsigned long byteOffset, optional unsigned long byteLength);
+
+  /**
+   * The readyState attribute indicates which state the socket is currently
+   * in. The state will be either "connecting", "open", "closing", or "closed".
+   */
+  readonly attribute DOMString readyState;
+
+  /**
+   * The binaryType attribute indicates which mode this socket uses for
+   * sending and receiving data. If the binaryType: "arraybuffer" option
+   * was passed to the open method that created this socket, binaryType
+   * will be "arraybuffer". Otherwise, it will be "string".
+   */
+  readonly attribute TCPSocketBinaryType binaryType;
+
+  /**
+   * The onopen event handler is called when the connection to the server
+   * has been established. If the connection is refused, onerror will be
+   * called, instead.
+   */
+  attribute EventHandler onopen;
+
+  /**
+   * After send has buffered more than 64k of data, it returns false to
+   * indicate that the client should pause before sending more data, to
+   * avoid accumulating large buffers. This is only advisory, and the client
+   * is free to ignore it and buffer as much data as desired, but if reducing
+   * the size of buffers is important (especially for a streaming application)
+   * ondrain will be called once the previously-buffered data has been written
+   * to the network, at which point the client can resume calling send again.
+   */
+  attribute EventHandler ondrain;
+
+  /**
+   * The ondata handler will be called repeatedly and asynchronously after
+   * onopen has been called, every time some data was available from the server
+   * and was read. If binaryType: "arraybuffer" was passed to open, the data
+   * attribute of the event object will be an ArrayBuffer. If not, it will be a
+   * normal JavaScript string.
+   *
+   * At any time, the client may choose to pause reading and receiving ondata
+   * callbacks, by calling the socket's suspend() method. Further invocations
+   * of ondata will be paused until resume() is called.
+   */
+  attribute EventHandler ondata;
+
+  /**
+   * The onerror handler will be called when there is an error. The data
+   * attribute of the event passed to the onerror handler will have a
+   * description of the kind of error.
+   *
+   * If onerror is called before onopen, the error was connection refused,
+   * and onclose will not be called. If onerror is called after onopen,
+   * the connection was lost, and onclose will be called after onerror.
+   */
+  attribute EventHandler onerror;
+
+  /**
+   * The onclose handler is called once the underlying network socket
+   * has been closed, either by the server, or by the client calling
+   * close.
+   *
+   * If onerror was not called before onclose, then either side cleanly
+   * closed the connection.
+   */
+  attribute EventHandler onclose;
+
+  [ChromeOnly]
+  nsITCPSocketInternal getInternalSocket();
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TCPSocketEvent.webidl
@@ -0,0 +1,34 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * TCPSocketEvent 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).
+ */
+
+[Constructor(DOMString type, optional TCPSocketEventInit eventInitDict),
+ Func="TCPSocketUtils::SocketEnabled", Exposed=(Window,System)]
+interface TCPSocketEvent : Event {
+  /**
+   * The data related to this event, if any. In the ondata callback,
+   * data will be the bytes read from the network; if the binaryType
+   * of the socket was "arraybuffer", this value will be of type ArrayBuffer;
+   * otherwise, it will be a normal JavaScript string.
+   *
+   * In the onerror callback, data will be a string with a description
+   * of the error.
+   *
+   * In the other callbacks, data will be an empty string.
+   */
+  //TODO: make this (ArrayBuffer or DOMString) after sorting out the rooting required. bug 1121634
+  readonly attribute any data;
+};
+
+dictionary TCPSocketEventInit : EventInit {
+  //TODO: make this (ArrayBuffer or DOMString) after sorting out the rooting required. bug 1121634
+  any data = null;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -490,16 +490,18 @@ WEBIDL_FILES = [
     'SVGUnitTypes.webidl',
     'SVGURIReference.webidl',
     'SVGUseElement.webidl',
     'SVGViewElement.webidl',
     'SVGZoomAndPan.webidl',
     'SVGZoomEvent.webidl',
     'TCPServerSocket.webidl',
     'TCPServerSocketEvent.webidl',
+    'TCPSocket.webidl',
+    'TCPSocketEvent.webidl',
     'Telephony.webidl',
     'TelephonyCall.webidl',
     'TelephonyCallGroup.webidl',
     'TelephonyCallId.webidl',
     'Text.webidl',
     'TextDecoder.webidl',
     'TextEncoder.webidl',
     'TextTrack.webidl',
@@ -734,16 +736,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'RTCPeerConnectionIdentityErrorEvent.webidl',
     'RTCPeerConnectionIdentityEvent.webidl',
     'ScrollViewChangeEvent.webidl',
     'SelectionStateChangedEvent.webidl',
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
     'StyleSheetChangeEvent.webidl',
     'TCPServerSocketEvent.webidl',
+    'TCPSocketEvent.webidl',
     'TrackEvent.webidl',
     'TVCurrentChannelChangedEvent.webidl',
     'TVCurrentSourceChangedEvent.webidl',
     'TVEITBroadcastedEvent.webidl',
     'TVScanningStateChangedEvent.webidl',
     'UDPMessageEvent.webidl',
     'UserProximityEvent.webidl',
     'USSDReceivedEvent.webidl',