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 238856 9ede577f5ada3f664d7a9bc13d2f617901349e00
parent 238855 6ccc86f7429e38dec4343a43f7d994c4c553e91e
child 238857 865e7bc208dfde6b92fdfd074995728de78189f7
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-esr52@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs885982
milestone38.0a1
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',