Bug 1118063 - Convert TCPServerSocket to WebIDL. r=baku,smaug,mayhemer,asuth
☠☠ backed out by 0c454540fc2b ☠ ☠
authorJosh Matthews <josh@joshmatthews.net>
Sat, 17 Jan 2015 11:57:41 -0500
changeset 238855 6ccc86f7429e38dec4343a43f7d994c4c553e91e
parent 238854 3e38ec9ed49a0f4afae366bfc13d36ba892c0037
child 238856 9ede577f5ada3f664d7a9bc13d2f617901349e00
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, smaug, mayhemer, asuth
bugs1118063
milestone38.0a1
Bug 1118063 - Convert TCPServerSocket to WebIDL. r=baku,smaug,mayhemer,asuth
dom/bindings/Bindings.conf
dom/network/TCPServerSocket.js
dom/network/TCPServerSocketChild.cpp
dom/network/TCPServerSocketParent.cpp
dom/network/TCPServerSocketParent.h
dom/network/TCPSocket.js
dom/network/TCPSocketParentIntermediary.js
dom/network/interfaces/moz.build
dom/network/interfaces/nsIDOMTCPServerSocket.idl
dom/network/interfaces/nsIDOMTCPSocket.idl
dom/network/interfaces/nsITCPServerSocketChild.idl
dom/network/interfaces/nsITCPServerSocketInternal.idl
dom/network/interfaces/nsITCPServerSocketParent.idl
dom/network/interfaces/nsITCPSocketParent.idl
dom/network/moz.build
dom/network/tests/test_tcpsocket_client_and_server_basics.js
dom/webidl/TCPServerSocket.webidl
dom/webidl/TCPServerSocketEvent.webidl
dom/webidl/moz.build
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1861,28 +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('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
@@ -6,91 +6,122 @@
 
 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");
 
 const ServerSocket = CC(
         '@mozilla.org/network/server-socket;1', 'nsIServerSocket', 'init'),
       TCPSocketInternal = Cc[
         '@mozilla.org/tcp-socket;1'].createInstance(Ci.nsITCPSocketInternal);
 
 /*
  * Debug logging function
  */
 
-let debug = true;
+let debug = false;
 function LOG(msg) {
   if (debug) {
     dump("TCPServerSocket: " + msg + "\n");
   }
 }
 
 /*
  * nsIDOMTCPServerSocket object
  */
 
 function TCPServerSocket() {
+  this.makeGetterSetterEH("onconnect");
+  this.makeGetterSetterEH("onerror");
+
   this._localPort = 0;
   this._binaryType = null;
 
-  this._onconnect = null;
-  this._onerror = null;
-
   this._inChild = false;
   this._neckoTCPServerSocket = null;
   this._serverBridge = null;
-  this.useWin = null;
 }
 
-// When this API moves to WebIDL and these __exposedProps__ go away, remove
-// this call here and remove the API from XPConnect.
-Cu.skipCOWCallableChecks();
-
 TCPServerSocket.prototype = {
-  __exposedProps__: {
-    localPort: 'r',
-    onconnect: 'rw',
-    onerror: 'rw'
-  },
   get localPort() {
     return this._localPort;
   },
-  get onconnect() {
-    return this._onconnect;
+
+  getEH: function(type) {
+    return this.__DOM_IMPL__.getEventHandler(type);
   },
-  set onconnect(f) {
-    this._onconnect = f;
+  setEH: function(type, handler) {
+    this.__DOM_IMPL__.setEventHandler(type, handler);
   },
-  get onerror() {
-    return this._onerror;
-  },
-  set onerror(f) {
-    this._onerror = f;
+  makeGetterSetterEH: function(name) {
+    Object.defineProperty(this, name,
+                          {
+                            get:function()  { return this.getEH(name); },
+                            set:function(h) { return this.setEH(name, h); }
+                          });
   },
 
   _callListenerAcceptCommon: function tss_callListenerAcceptCommon(socket) {
-    if (this._onconnect) {
-      try {
-        this["onconnect"].call(null, socket);
-      } catch (e) {
-        socket.close();
-      }
-    }
-    else {
+    try {
+      this.__DOM_IMPL__.dispatchEvent(new this.useGlobal.TCPServerSocketEvent("connect", {socket: socket}));
+    } catch(e) {
+      LOG('exception in _callListenerAcceptCommon: ' + e);
       socket.close();
-      dump("Received unexpected connection!");
     }
   },
   init: function tss_init(aWindowObj) {
-    this.useWin = aWindowObj;
+    this.useGlobal = aWindowObj;
+
+    if (aWindowObj) {
+      Services.obs.addObserver(this, "inner-window-destroyed", true);
+
+      let util = aWindowObj.QueryInterface(
+        Ci.nsIInterfaceRequestor
+      ).getInterface(Ci.nsIDOMWindowUtils);
+      this.innerWindowID = util.currentInnerWindowID;
+
+      LOG("window init: " + this.innerWindowID);
+    }
+  },
+
+  __init: function(port, options, backlog) {
+    this.listen(port, options, backlog);
+  },
+
+  initWithGlobal: function(global) {
+    this.useGlobal = global;
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    let wId;
+    try {
+      wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+    } catch(x) {
+      wId = 0;
+    }
+
+    if (wId == this.innerWindowID) {
+      LOG("inner-window-destroyed: " + this.innerWindowID);
+
+      // This window is now dead, so we want to clear the callbacks
+      // so that we don't get a "can't access dead object" when the
+      // underlying stream goes to tell us that we are closed
+      this.onconnect = null;
+      this.onerror = null;
+
+      this.useGlobal = null;
+
+      // Clean up our socket
+      this.close();
+    }
   },
 
   /* nsITCPServerSocketInternal method */
   listen: function tss_listen(localPort, options, backlog) {
     this._inChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
                        .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
     this._binaryType = options.binaryType;
 
@@ -113,27 +144,28 @@ 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.useWin);
+    let socket = TCPSocketInternal.createAcceptedChild(socketChild, this._binaryType, this.useGlobal);
     this._callListenerAcceptCommon(socket);
   },
 
   callListenerError: function tss_callListenerError(message, filename, lineNumber, columnNumber) {
-    if (this._onerror) {
-      var type = "error";
-      var error = new Error(message, filename, lineNumber, columnNumber);
-
-      this["onerror"].call(null, new TCPSocketEvent(type, this, error));
-    }
+    let init = {
+      message: message,
+      filename: filename,
+      lineno: lineNumber,
+      colno: columnNumber,
+    };
+    this.__DOM_IMPL__.dispatchEvent(new this.useGlobal.ErrorEvent("error", init));
   },
   /* end nsITCPServerSocketInternal method */
 
   close: function tss_close() {
     if (this._inChild) {
       this._serverBridge.close();
       return;
     }
@@ -144,44 +176,41 @@ TCPServerSocket.prototype = {
     }
   },
 
   // nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen)
   onSocketAccepted: function tss_onSocketAccepted(server, trans) {
     // precondition: this._inChild == false
     try {
       let that = TCPSocketInternal.createAcceptedParent(trans, this._binaryType,
-                                                        this.useWin);
+                                                        this.useGlobal);
       this._callListenerAcceptCommon(that);
     }
     catch(e) {
+      LOG('exception in onSocketAccepted: ' + e);
       trans.close(Cr.NS_BINDING_ABORTED);
     }
   },
 
   // nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen)
   onStopListening: function tss_onStopListening(server, status) {
     if (status != Cr.NS_BINDING_ABORTED) {
       throw new Error("Server socket was closed by unexpected reason.");
     }
     this._neckoTCPServerSocket = null;
   },
 
+  getInternalSocket: function() {
+    return this.QueryInterface(Ci.nsITCPServerSocketInternal);
+  },
+
   classID: Components.ID("{73065eae-27dc-11e2-895a-000c29987aa2}"),
 
-  classInfo: XPCOMUtils.generateCI({
-    classID: Components.ID("{73065eae-27dc-11e2-895a-000c29987aa2}"),
-    classDescription: "Server TCP Socket",
-    interfaces: [
-      Ci.nsIDOMTCPServerSocket,
-      Ci.nsISupportsWeakReference
-    ],
-    flags: Ci.nsIClassInfo.DOM_OBJECT,
-  }),
-
+  contractID: "@mozilla.org/tcp-server-socket;1",
   QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIDOMTCPServerSocket,
     Ci.nsITCPServerSocketInternal,
-    Ci.nsISupportsWeakReference
+    Ci.nsIDOMGlobalPropertyInitializer,
+    Ci.nsISupportsWeakReference,
+    Ci.nsIObserver
   ])
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TCPServerSocket]);
--- a/dom/network/TCPServerSocketChild.cpp
+++ b/dom/network/TCPServerSocketChild.cpp
@@ -2,17 +2,17 @@
  * 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 "TCPServerSocketChild.h"
 #include "TCPSocketChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/TabChild.h"
-#include "nsIDOMTCPSocket.h"
+#include "nsITCPServerSocketInternal.h"
 #include "nsJSUtils.h"
 #include "jsfriendapi.h"
 
 using mozilla::net::gNeckoChild;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/network/TCPServerSocketParent.cpp
+++ b/dom/network/TCPServerSocketParent.cpp
@@ -2,22 +2,42 @@
  * 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);
 }
@@ -26,16 +46,26 @@ NS_IMPL_CYCLE_COLLECTION(TCPServerSocket
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPServerSocketParent)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPServerSocketParent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPServerSocketParent)
   NS_INTERFACE_MAP_ENTRY(nsITCPServerSocketParent)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
+TCPServerSocketParent::TCPServerSocketParent()
+: mNeckoParent(nullptr)
+, mIPCOpen(false)
+{
+}
+
+TCPServerSocketParent::~TCPServerSocketParent()
+{
+}
+
 void
 TCPServerSocketParent::ReleaseIPDLReference()
 {
   MOZ_ASSERT(mIPCOpen);
   mIPCOpen = false;
   this->Release();
 }
 
--- a/dom/network/TCPServerSocketParent.h
+++ b/dom/network/TCPServerSocketParent.h
@@ -1,52 +1,60 @@
 /* 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 mozilla_dom_TCPServerSocketParent_h
+#define mozilla_dom_TCPServerSocketParent_h
+
 #include "mozilla/net/PNeckoParent.h"
 #include "mozilla/net/PTCPServerSocketParent.h"
 #include "nsITCPSocketParent.h"
 #include "nsITCPServerSocketParent.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
-#include "nsIDOMTCPSocket.h"
+
+class nsITCPServerSocketInternal;
 
 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() : mNeckoParent(nullptr), mIPCOpen(false) {}
+  TCPServerSocketParent();
 
   bool Init(PNeckoParent* neckoParent, const uint16_t& aLocalPort, const uint16_t& aBacklog,
             const nsString& aBinaryType);
 
   virtual bool RecvClose() MOZ_OVERRIDE;
   virtual bool RecvRequestDelete() MOZ_OVERRIDE;
 
   uint32_t GetAppId();
   bool GetInBrowser();
 
   void AddIPDLReference();
   void ReleaseIPDLReference();
 
 private:
-  ~TCPServerSocketParent() {}
+  ~TCPServerSocketParent();
 
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   PNeckoParent* mNeckoParent;
   nsCOMPtr<nsITCPSocketIntermediary> mIntermediary;
-  nsCOMPtr<nsIDOMTCPServerSocket> mServerSocket;
+  nsCOMPtr<nsITCPServerSocketInternal> mServerSocket;
   bool mIPCOpen;
 };
 
 } // namespace dom
 } // namespace mozilla
+
+#endif // mozilla_dom_TCPServerSocketParent_h
--- a/dom/network/TCPSocket.js
+++ b/dom/network/TCPSocket.js
@@ -129,17 +129,16 @@ TCPSocket.prototype = {
     ssl: 'r',
     bufferedAmount: 'r',
     suspend: 'r',
     resume: 'r',
     close: 'r',
     send: 'r',
     readyState: 'r',
     binaryType: 'r',
-    listen: 'r',
     onopen: 'rw',
     ondrain: 'rw',
     ondata: 'rw',
     onerror: 'rw',
     onclose: 'rw'
   },
   // The binary type, "string" or "arraybuffer"
   _binaryType: null,
@@ -646,30 +645,16 @@ TCPSocket.prototype = {
 
     if (this._multiplexStream.count == 0) {
       this._activateTLS();
     } else {
       this._waitingForStartTLS = true;
     }
   },
 
-  listen: function ts_listen(localPort, options, backlog) {
-    // 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 TCPServerSocket(this.useWin);
-
-    options = options || { binaryType : this.binaryType };
-    backlog = backlog || -1;
-    that.listen(localPort, options, backlog);
-    return that;
-  },
-
   close: function ts_close() {
     if (this._readyState === kCLOSED || this._readyState === kCLOSING)
       return;
 
     LOG("close called");
     this._readyState = kCLOSING;
 
     if (this._inChild) {
--- a/dom/network/TCPSocketParentIntermediary.js
+++ b/dom/network/TCPSocketParentIntermediary.js
@@ -4,16 +4,19 @@
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+let global = this;
 
 function TCPSocketParentIntermediary() {
 }
 
 TCPSocketParentIntermediary.prototype = {
   _setCallbacks: function(aParentSide, socket) {
     aParentSide.initJS(this);
     this._socket = socket;
@@ -53,53 +56,52 @@ TCPSocketParentIntermediary.prototype = 
 
     // Handlers are set to the JS-implemented socket object on the parent side.
     this._setCallbacks(aParentSide, socket);
     return socket;
   },
 
   listen: function(aTCPServerSocketParent, aLocalPort, aBacklog, aBinaryType,
                    aAppId, aInBrowser) {
-    let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
-    let serverSocket = baseSocket.listen(aLocalPort, { binaryType: aBinaryType }, aBacklog);
-    if (!serverSocket)
-      return null;
+    let serverSocket = new global.mozTCPServerSocket(aLocalPort, { binaryType: aBinaryType }, aBacklog);
+    let serverSocketInternal = serverSocket.getInternalSocket();
+    serverSocketInternal.initWithGlobal(global);
 
     let localPort = serverSocket.localPort;
 
-    serverSocket["onconnect"] = function(socket) {
+    serverSocket["onconnect"] = function(event) {
       var socketParent = Cc["@mozilla.org/tcp-socket-parent;1"]
                             .createInstance(Ci.nsITCPSocketParent);
       var intermediary = new TCPSocketParentIntermediary();
 
-      let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal);
+      let socketInternal = event.socket.QueryInterface(Ci.nsITCPSocketInternal);
       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, socket);
+      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(socket, intermediary);
+      socketParent.setSocketAndIntermediary(event.socket, intermediary);
       aTCPServerSocketParent.sendCallbackAccept(socketParent);
     };
 
     serverSocket["onerror"] = function(data) {
         var error = data.data;
 
         aTCPServerSocketParent.sendCallbackError(error.message, error.filename,
                                                  error.lineNumber, error.columnNumber);
     };
 
-    return serverSocket;
+    return serverSocketInternal;
   },
 
   onRecvSendString: function(aData, aTrackingNumber) {
     let socketInternal = this._socket.QueryInterface(Ci.nsITCPSocketInternal);
     return socketInternal.onRecvSendFromChild(aData, 0, 0, aTrackingNumber);
   },
 
   onRecvSendArrayBuffer: function(aData, aTrackingNumber) {
--- a/dom/network/interfaces/moz.build
+++ b/dom/network/interfaces/moz.build
@@ -1,19 +1,19 @@
 # -*- 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 += [
-    'nsIDOMTCPServerSocket.idl',
     'nsIDOMTCPSocket.idl',
     'nsIMozNavigatorNetwork.idl',
     'nsITCPServerSocketChild.idl',
+    'nsITCPServerSocketInternal.idl',
     'nsITCPServerSocketParent.idl',
     'nsITCPSocketChild.idl',
     'nsITCPSocketParent.idl',
     'nsIUDPSocketChild.idl',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     XPIDL_SOURCES += [
--- a/dom/network/interfaces/nsIDOMTCPSocket.idl
+++ b/dom/network/interfaces/nsIDOMTCPSocket.idl
@@ -7,17 +7,16 @@
  * 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"
-#include "nsIDOMTCPServerSocket.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"
 
@@ -27,17 +26,17 @@ interface nsISocketTransport;
 //  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(65f6d2c8-4be6-4695-958d-0735e8935289)]
+[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.
@@ -50,36 +49,16 @@ interface nsIDOMTCPSocket : nsISupports
    *          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);
 
   /**
-   * Listen on a port
-   *
-   * @param localPort The port of the server socket. Pass -1 to indicate no preference,
-   *                  and a port will be selected automatically.
-   * @param options An object specifying one or more parameters which
-   *                determine the details of the socket.
-   *
-   *        binaryType: "arraybuffer" to use ArrayBuffer
-   *          instances in the ondata callback and as the argument
-   *          to send. Defaults to "string", to use JavaScript strings.
-   * @param backlog The maximum length the queue of pending connections may grow to.
-   *                This parameter may be silently limited by the operating system.
-   *                Pass -1 to use the default value.
-   *
-   * @return The new TCPServerSocket instance.
-   */
-  nsIDOMTCPServerSocket listen(in unsigned short localPort, [optional] in jsval options,
-                               [optional] in unsigned short backlog);
-
-  /**
    * Enable secure on channel.
    */
   void upgradeToSecure();
 
   /**
    * The host of this socket object.
    */
   readonly attribute DOMString host;
@@ -242,27 +221,27 @@ interface nsITCPSocketInternal : nsISupp
   // @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 
+  //        "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.
+  // @param global
+  //        An object to create ArrayBuffer for this global. See Bug 831107.
   nsIDOMTCPSocket createAcceptedParent(in nsISocketTransport transport,
                                        in DOMString binaryType,
-                                       in nsIDOMWindow window);
+                                       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
--- a/dom/network/interfaces/nsITCPServerSocketChild.idl
+++ b/dom/network/interfaces/nsITCPServerSocketChild.idl
@@ -1,14 +1,13 @@
 /* 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"
-#include "nsIDOMTCPServerSocket.idl"
 
 interface nsITCPServerSocketInternal;
 
 /**
  * Interface to allow the content process server socket to reach the IPC bridge.
  * It is used in the server socket implementation on the child side.
  */
 
rename from dom/network/interfaces/nsIDOMTCPServerSocket.idl
rename to dom/network/interfaces/nsITCPServerSocketInternal.idl
--- a/dom/network/interfaces/nsIDOMTCPServerSocket.idl
+++ b/dom/network/interfaces/nsITCPServerSocketInternal.idl
@@ -1,68 +1,32 @@
 /* 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"
-#include "nsITCPSocketChild.idl"
 
-// Bug 797561 - Expose a server tcp socket API to web applications
-/**
- * nsIDOMTCPServerSocket
- *
- * An interface to a server socket that can accept incoming connections for gaia apps.
- */
-[scriptable, uuid(821638a1-5327-416d-8031-668764f2ec04)]
-interface nsIDOMTCPServerSocket : nsISupports
-{
-  /**
-   * The port of this server socket object.
-   */
-  readonly attribute unsigned short localPort;
-
-  /**
-   * The onconnect event handler is called when a client connection is accepted.
-   * The data attribute of the event passed to the onconnect handler will be a TCPSocket
-   * instance, which is used for communication between client and server. 
-   */
-  attribute jsval onconnect;
-
-  /**
-   * The onerror handler will be called when the listen of a server socket is aborted.
-   * The data attribute of the event passed to the onerror handler will have a
-   * description of the kind of error.
-   */
-  attribute jsval onerror;
-
-  /**
-   * Close the server socket.
-   */
-  void close();
-};
+interface nsITCPSocketChild;
 
 /**
  * Internal interfaces for use in cross-process server-socket implementation.
  * Needed to account for multiple possible types that can be provided to
  * the socket callbacks as arguments.
  *
  * These interfaces are for calling each method from the server socket object
  * on the parent and child side for an IPC protocol implementation.
  */
 
-[scriptable, uuid(b64b1e68-4efa-497c-b0d8-69f067ad5ec8)]
-interface nsITCPServerSocketInternal : nsISupports 
+[scriptable, uuid(03520F9E-7989-4604-870A-A67DFFD846DC)]
+interface nsITCPServerSocketInternal : nsISupports
 {
   /**
-   * Initialization after creating a TCP server socket object.
-   *
-   * @param windowVal
-   *        An object to create ArrayBuffer for this window. See Bug 831107.
+   * Close the server socket.
    */
-  void init(in jsval windowVal);
+  void close();
 
   /** 
    * Listen on a port
    *
    * @param localPort 
    *        The port of the server socket. Pass -1 to indicate no preference,
    *        and a port will be selected automatically.
    * @param options 
@@ -84,9 +48,14 @@ interface nsITCPServerSocketInternal : n
    */
   void callListenerAccept(in nsITCPSocketChild socketChild);
 
   /**
    * Listener for handling an error caused in chrome process.
    */
   void callListenerError(in DOMString message, in DOMString filename,
                          in uint32_t lineNumber, in uint32_t columnNumber);
+
+  /**
+   * Called by the parent process when there is no DOM window available.
+   */
+  void initWithGlobal(in nsISupports global);
 };
--- a/dom/network/interfaces/nsITCPServerSocketParent.idl
+++ b/dom/network/interfaces/nsITCPServerSocketParent.idl
@@ -1,18 +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 "domstubs.idl"
-#include "nsITCPSocketParent.idl"
 
-interface nsIDOMTCPServerSocket;
+interface nsITCPSocketParent;
 
-/** 
+/**
  * Interface required to allow the TCP server-socket object in the parent process
  * to talk to the parent IPC actor.
  * It is used in the server socket implementation on the parent side.
  */
 [scriptable, uuid(161ffc9f-54d3-4f21-a536-4166003d0e1d)]
 interface nsITCPServerSocketParent : nsISupports
 {
   /**
--- a/dom/network/interfaces/nsITCPSocketParent.idl
+++ b/dom/network/interfaces/nsITCPSocketParent.idl
@@ -1,23 +1,23 @@
 /* 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 nsIDOMTCPServerSocket;
+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(6f040bf0-6852-11e3-949a-0800200c9a66)]
+[scriptable, uuid(03B864C8-AAA9-4062-943E-AE6397070E0D)]
 interface nsITCPSocketParent : nsISupports
 {
   [implicit_jscontext] void initJS(in jsval intermediary);
 
   // Trigger a callback in the content process for |type|, providing a serialized
   // argument of |data|, and update the child's readyState value with the given
   // values.
   //
@@ -70,20 +70,20 @@ interface nsITCPSocketIntermediary : nsI
   // 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);
 
   // Listen on a port
-  nsIDOMTCPServerSocket listen(in nsITCPServerSocketParent parent,
-                               in unsigned short port, in unsigned short backlog,
-                               in DOMString binaryType,
-                               in unsigned long appId,
-                               in boolean inBrowser);
+  nsITCPServerSocketInternal listen(in nsITCPServerSocketParent parent,
+                                    in unsigned short port, in unsigned short backlog,
+                                    in DOMString binaryType,
+                                    in unsigned long appId,
+                                    in boolean inBrowser);
 
   // Called when received a child request to send a string.
   void onRecvSendString(in DOMString data, in uint32_t trackingNumber);
 
   // Called when received a child request to send an array buffer.
   void onRecvSendArrayBuffer(in jsval data, in uint32_t trackingNumber);
 };
--- a/dom/network/moz.build
+++ b/dom/network/moz.build
@@ -11,16 +11,20 @@ XPCSHELL_TESTS_MANIFESTS += [
     'tests/unit_ipc/xpcshell.ini',
 ]
 
 if CONFIG['MOZ_B2G_RIL']:
     XPCSHELL_TESTS_MANIFESTS += ['tests/unit_stats/xpcshell.ini']
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 
+EXPORTS += [
+    'TCPServerSocketParent.h',
+]
+
 EXPORTS.mozilla.dom += [
     'UDPSocket.h',
 ]
 
 EXPORTS.mozilla.dom.network += [
     'Connection.h',
     'Constants.h',
     'TCPServerSocketChild.h',
@@ -76,11 +80,13 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
 IPDL_SOURCES += [
     'PTCPServerSocket.ipdl',
     'PTCPSocket.ipdl',
     'PUDPSocket.ipdl',
 ]
 
 FAIL_ON_WARNINGS = True
 
+LOCAL_INCLUDES += ['/dom/base']
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -122,28 +122,32 @@ function listenForEventsOnSocket(socket,
  * calling listenForEventsOnSocket(socket).  This must be done because we need
  * to add the event listener during the connection.
  */
 function waitForConnection(listeningServer) {
   return new Promise(function(resolve, reject) {
     // Because of the event model of sockets, we can't use the
     // listenForEventsOnSocket mechanism; we need to hook up listeners during
     // the connect event.
-    listeningServer.onconnect = function(socket) {
+    listeningServer.onconnect = function(ev) {
       // Clobber the listener to get upset if it receives any more connections
       // after this.
       listeningServer.onconnect = function() {
         ok(false, 'Received a connection when not expecting one.');
       };
       ok(true, 'Listening server accepted socket');
       resolve({
-        socket: socket,
-        queue: listenForEventsOnSocket(socket, 'server')
+        socket: ev.socket,
+        queue: listenForEventsOnSocket(ev.socket, 'server')
       });
     };
+    listeningServer.onerror = function(ev) {
+      ok(false, 'Received an error when not expecting one.');
+      reject();
+    };
   });
 }
 
 function defer() {
   var deferred = {};
   deferred.promise = new Promise(function(resolve, reject) {
     deferred.resolve = resolve;
     deferred.reject = reject;
@@ -168,19 +172,19 @@ function* test_basics() {
 
   // 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 = TCPSocket.listen(serverPort,
-                                         { binaryType: 'arraybuffer' },
-                                         SERVER_BACKLOG);
+  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 clientQueue = listenForEventsOnSocket(clientSocket, 'client');
 
@@ -338,17 +342,17 @@ function* test_basics() {
 
   // The server will get its data
   serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
     bigUint8Array.length);
   assertUint8ArraysEqual(serverReceived, bigUint8Array,
                          'server received/client sent');
   // And a close.
   is((yield serverQueue.waitForEvent()).type, 'close',
-     'The drain event should fire after a large send that returned true.');
+     'The close event should fire after a large send that returned true.');
 
 
   // -- 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
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TCPServerSocket.webidl
@@ -0,0 +1,53 @@
+/* -*- 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 nsITCPServerSocketInternal;
+
+/**
+ * mozTCPServerSocket
+ *
+ * An interface to a server socket that can accept incoming connections for gaia apps.
+ */
+
+enum TCPServerSocketBinaryType {
+  "arraybuffer",
+  "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",
+ Exposed=(Window,System)]
+interface mozTCPServerSocket : EventTarget {
+  /**
+   * The port of this server socket object.
+   */
+  readonly attribute unsigned short localPort;
+
+  /**
+   * The onconnect event handler is called when a client connection is accepted.
+   * The socket attribute of the event passed to the onconnect handler will be a TCPSocket
+   * instance, which is used for communication between client and server.
+   */
+  attribute EventHandler onconnect;
+
+  /**
+   * The onerror handler will be called when the listen of a server socket is aborted.
+   * The data attribute of the event passed to the onerror handler will have a
+   * description of the kind of error.
+   */
+  attribute EventHandler onerror;
+
+  /**
+   * Close the server socket.
+   */
+  void close();
+
+  [ChromeOnly]
+  nsITCPServerSocketInternal getInternalSocket();
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TCPServerSocketEvent.webidl
@@ -0,0 +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)]
+interface TCPServerSocketEvent : Event {
+  readonly attribute nsIDOMTCPSocket socket; // mozTCPSocket
+};
+
+dictionary TCPServerSocketEventInit : EventInit {
+  nsIDOMTCPSocket? socket = null;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -488,16 +488,18 @@ WEBIDL_FILES = [
     'SVGTransformList.webidl',
     'SVGTSpanElement.webidl',
     'SVGUnitTypes.webidl',
     'SVGURIReference.webidl',
     'SVGUseElement.webidl',
     'SVGViewElement.webidl',
     'SVGZoomAndPan.webidl',
     'SVGZoomEvent.webidl',
+    'TCPServerSocket.webidl',
+    'TCPServerSocketEvent.webidl',
     'Telephony.webidl',
     'TelephonyCall.webidl',
     'TelephonyCallGroup.webidl',
     'TelephonyCallId.webidl',
     'Text.webidl',
     'TextDecoder.webidl',
     'TextEncoder.webidl',
     'TextTrack.webidl',
@@ -731,16 +733,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'RTCPeerConnectionIceEvent.webidl',
     'RTCPeerConnectionIdentityErrorEvent.webidl',
     'RTCPeerConnectionIdentityEvent.webidl',
     'ScrollViewChangeEvent.webidl',
     'SelectionStateChangedEvent.webidl',
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
     'StyleSheetChangeEvent.webidl',
+    'TCPServerSocketEvent.webidl',
     'TrackEvent.webidl',
     'TVCurrentChannelChangedEvent.webidl',
     'TVCurrentSourceChangedEvent.webidl',
     'TVEITBroadcastedEvent.webidl',
     'TVScanningStateChangedEvent.webidl',
     'UDPMessageEvent.webidl',
     'UserProximityEvent.webidl',
     'USSDReceivedEvent.webidl',