From 53a5e2bb9cd8cade61a283ed2af64d4dc50606fd Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sat, 17 Jan 2015 13:26:10 -0800 Subject: [PATCH] Back out 3 changesets (bug 885982, bug 1118063) for b2g mochitest-6 bustage CLOSED TREE Backed out changeset 865e7bc208df (bug 885982) Backed out changeset 9ede577f5ada (bug 885982) Backed out changeset 6ccc86f7429e (bug 1118063) --HG-- rename : dom/network/interfaces/nsITCPServerSocketInternal.idl => dom/network/interfaces/nsIDOMTCPServerSocket.idl --- CLOBBER | 2 +- dom/bindings/Bindings.conf | 2 - dom/network/TCPServerSocket.js | 138 +++---- dom/network/TCPServerSocketChild.cpp | 2 +- dom/network/TCPServerSocketParent.cpp | 13 - dom/network/TCPServerSocketParent.h | 14 +- dom/network/TCPSocket.js | 373 ++++++++++++------ dom/network/TCPSocket.manifest | 1 + dom/network/TCPSocketChild.cpp | 2 +- dom/network/TCPSocketParent.cpp | 11 +- dom/network/TCPSocketParent.h | 5 +- dom/network/TCPSocketParentIntermediary.js | 32 +- dom/network/TCPSocketUtils.cpp | 31 -- dom/network/TCPSocketUtils.h | 17 - dom/network/interfaces/moz.build | 4 +- ...Internal.idl => nsIDOMTCPServerSocket.idl} | 51 ++- dom/network/interfaces/nsIDOMTCPSocket.idl | 333 ++++++++++++++++ .../interfaces/nsITCPServerSocketChild.idl | 1 + .../interfaces/nsITCPServerSocketParent.idl | 5 +- .../interfaces/nsITCPSocketInternal.idl | 133 ------- dom/network/interfaces/nsITCPSocketParent.idl | 30 +- dom/network/moz.build | 7 - dom/network/tests/mochitest.ini | 2 + ...test_tcpsocket_client_and_server_basics.js | 35 +- .../test_tcpsocket_default_permissions.html | 7 +- .../tests/test_tcpsocket_enabled_no_perm.html | 35 ++ .../test_tcpsocket_enabled_with_perm.html | 8 +- dom/network/tests/unit/test_tcpsocket.js | 9 +- .../tests/unit_ipc/test_tcpsocket_ipc.js | 2 +- dom/permission/tests/test_tcp-socket.html | 12 +- dom/webidl/TCPServerSocket.webidl | 53 --- dom/webidl/TCPServerSocketEvent.webidl | 15 - dom/webidl/TCPSocket.webidl | 162 -------- dom/webidl/TCPSocketEvent.webidl | 34 -- dom/webidl/moz.build | 6 - 35 files changed, 812 insertions(+), 775 deletions(-) delete mode 100644 dom/network/TCPSocketUtils.cpp delete mode 100644 dom/network/TCPSocketUtils.h rename dom/network/interfaces/{nsITCPServerSocketInternal.idl => nsIDOMTCPServerSocket.idl} (58%) create mode 100644 dom/network/interfaces/nsIDOMTCPSocket.idl delete mode 100644 dom/network/interfaces/nsITCPSocketInternal.idl create mode 100644 dom/network/tests/test_tcpsocket_enabled_no_perm.html delete mode 100644 dom/webidl/TCPServerSocket.webidl delete mode 100644 dom/webidl/TCPServerSocketEvent.webidl delete mode 100644 dom/webidl/TCPSocket.webidl delete mode 100644 dom/webidl/TCPSocketEvent.webidl diff --git a/CLOBBER b/CLOBBER index dee4a962ab9f..14ab8a19e9a5 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Renaming nsIDOMTCPSocket.idl and nsIDOMTCPServerSocket.idl for bug 885982 and bug 1118063. +Bug 1121297 - Converted VolatileBuffer's CPP tests to GTests diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index baaee67be019..5b4c8f2c0a1c 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1878,8 +1878,6 @@ 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', diff --git a/dom/network/TCPServerSocket.js b/dom/network/TCPServerSocket.js index d05de8c52531..bea8308b7b22 100644 --- a/dom/network/TCPServerSocket.js +++ b/dom/network/TCPServerSocket.js @@ -11,7 +11,6 @@ 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'), @@ -22,7 +21,7 @@ const ServerSocket = CC( * Debug logging function */ -let debug = false; +let debug = true; function LOG(msg) { if (debug) { dump("TCPServerSocket: " + msg + "\n"); @@ -34,89 +33,59 @@ function LOG(msg) { */ 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; }, - - getEH: function(type) { - return this.__DOM_IMPL__.getEventHandler(type); + get onconnect() { + return this._onconnect; }, - setEH: function(type, handler) { - this.__DOM_IMPL__.setEventHandler(type, handler); + set onconnect(f) { + this._onconnect = f; }, - makeGetterSetterEH: function(name) { - Object.defineProperty(this, name, - { - get:function() { return this.getEH(name); }, - set:function(h) { return this.setEH(name, h); } - }); + get onerror() { + return this._onerror; + }, + set onerror(f) { + this._onerror = f; }, _callListenerAcceptCommon: function tss_callListenerAcceptCommon(socket) { - try { - this.__DOM_IMPL__.dispatchEvent(new this.useGlobal.TCPServerSocketEvent("connect", {socket: socket})); - } catch(e) { - LOG('exception in _callListenerAcceptCommon: ' + e); + if (this._onconnect) { + try { + this["onconnect"].call(null, socket); + } catch (e) { + socket.close(); + } + } + else { socket.close(); + dump("Received unexpected connection!"); } }, init: function tss_init(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(); - } + this.useWin = aWindowObj; }, /* nsITCPServerSocketInternal method */ @@ -149,19 +118,17 @@ TCPServerSocket.prototype = { callListenerAccept: function tss_callListenerSocket(socketChild) { // this method is called at child process when the socket is accepted at parent process. - let socket = new this.useGlobal.mozTCPSocket("", 0, {doNotConnect: true}); - socket.getInternalSocket().initAcceptedChild(socketChild, this._binaryType, this.useGlobal); + let socket = TCPSocketInternal.createAcceptedChild(socketChild, this._binaryType, this.useWin); this._callListenerAcceptCommon(socket); }, callListenerError: function tss_callListenerError(message, filename, lineNumber, columnNumber) { - let init = { - message: message, - filename: filename, - lineno: lineNumber, - colno: columnNumber, - }; - this.__DOM_IMPL__.dispatchEvent(new this.useGlobal.ErrorEvent("error", init)); + if (this._onerror) { + var type = "error"; + var error = new Error(message, filename, lineNumber, columnNumber); + + this["onerror"].call(null, new TCPSocketEvent(type, this, error)); + } }, /* end nsITCPServerSocketInternal method */ @@ -181,12 +148,11 @@ TCPServerSocket.prototype = { onSocketAccepted: function tss_onSocketAccepted(server, trans) { // precondition: this._inChild == false try { - let that = new this.useGlobal.mozTCPSocket("", 0, {doNotConnect: true}); - that.getInternalSocket().initAcceptedParent(trans, this._binaryType, this.useGlobal); + let that = TCPSocketInternal.createAcceptedParent(trans, this._binaryType, + this.useWin); this._callListenerAcceptCommon(that); } catch(e) { - LOG('exception in onSocketAccepted: ' + e); trans.close(Cr.NS_BINDING_ABORTED); } }, @@ -199,18 +165,22 @@ TCPServerSocket.prototype = { this._neckoTCPServerSocket = null; }, - getInternalSocket: function() { - return this.QueryInterface(Ci.nsITCPServerSocketInternal); - }, - classID: Components.ID("{73065eae-27dc-11e2-895a-000c29987aa2}"), - contractID: "@mozilla.org/tcp-server-socket;1", + 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, + }), + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIDOMTCPServerSocket, Ci.nsITCPServerSocketInternal, - Ci.nsIDOMGlobalPropertyInitializer, - Ci.nsISupportsWeakReference, - Ci.nsIObserver + Ci.nsISupportsWeakReference ]) } diff --git a/dom/network/TCPServerSocketChild.cpp b/dom/network/TCPServerSocketChild.cpp index ebb9ff45b969..05bccbef1135 100644 --- a/dom/network/TCPServerSocketChild.cpp +++ b/dom/network/TCPServerSocketChild.cpp @@ -7,7 +7,7 @@ #include "mozilla/net/NeckoChild.h" #include "mozilla/dom/PBrowserChild.h" #include "mozilla/dom/TabChild.h" -#include "nsITCPServerSocketInternal.h" +#include "nsIDOMTCPSocket.h" #include "nsJSUtils.h" #include "jsfriendapi.h" diff --git a/dom/network/TCPServerSocketParent.cpp b/dom/network/TCPServerSocketParent.cpp index 7fe75d76fb5b..ae470fa723bb 100644 --- a/dom/network/TCPServerSocketParent.cpp +++ b/dom/network/TCPServerSocketParent.cpp @@ -7,11 +7,8 @@ #include "TCPSocketParent.h" #include "mozilla/unused.h" #include "mozilla/AppProcessChecker.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 { @@ -34,16 +31,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPServerSocketParent) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -TCPServerSocketParent::TCPServerSocketParent() -: mNeckoParent(nullptr) -, mIPCOpen(false) -{ -} - -TCPServerSocketParent::~TCPServerSocketParent() -{ -} - void TCPServerSocketParent::ReleaseIPDLReference() { diff --git a/dom/network/TCPServerSocketParent.h b/dom/network/TCPServerSocketParent.h index 6558603ab772..a6455c3e1af8 100644 --- a/dom/network/TCPServerSocketParent.h +++ b/dom/network/TCPServerSocketParent.h @@ -2,17 +2,13 @@ * 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" - -class nsITCPServerSocketInternal; +#include "nsIDOMTCPSocket.h" namespace mozilla { namespace dom { @@ -27,7 +23,7 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSITCPSERVERSOCKETPARENT - TCPServerSocketParent(); + TCPServerSocketParent() : mNeckoParent(nullptr), mIPCOpen(false) {} bool Init(PNeckoParent* neckoParent, const uint16_t& aLocalPort, const uint16_t& aBacklog, const nsString& aBinaryType); @@ -42,17 +38,15 @@ public: void ReleaseIPDLReference(); private: - ~TCPServerSocketParent(); + ~TCPServerSocketParent() {} virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; PNeckoParent* mNeckoParent; nsCOMPtr mIntermediary; - nsCOMPtr mServerSocket; + nsCOMPtr mServerSocket; bool mIPCOpen; }; } // namespace dom } // namespace mozilla - -#endif // mozilla_dom_TCPServerSocketParent_h diff --git a/dom/network/TCPSocket.js b/dom/network/TCPSocket.js index f00c28c79144..9fa6e35c14eb 100644 --- a/dom/network/TCPSocket.js +++ b/dom/network/TCPSocket.js @@ -13,10 +13,6 @@ 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( @@ -49,7 +45,7 @@ const NETWORK_STATS_THRESHOLD = 65536; // 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 DOMError(aErrorName); + return new (aWindow ? aWindow.DOMError : DOMError)(aErrorName); } @@ -63,30 +59,94 @@ function LOG(msg) { 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.makeGetterSetterEH("onopen"); - this.makeGetterSetterEH("onclose"); - this.makeGetterSetterEH("ondrain"); - this.makeGetterSetterEH("ondata"); - this.makeGetterSetterEH("onerror"); - this._readyState = kCLOSED; + this._onopen = null; + this._ondrain = null; + this._ondata = null; + this._onerror = null; + this._onclose = null; + 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', + listen: '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, @@ -150,21 +210,36 @@ TCPSocket.prototype = { } return this._multiplexStream.available(); }, - - 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); } - }); - }, - + 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; + }, _activateTLS: function() { let securityInfo = this._transport.securityInfo @@ -180,7 +255,9 @@ TCPSocket.prototype = { } else { options = ['starttls']; } - return gSocketTransportService.createTransport(options, 1, host, port, null); + return Cc["@mozilla.org/network/socket-transport-service;1"] + .getService(Ci.nsISocketTransportService) + .createTransport(options, 1, host, port, null); }, _sendBufferedAmount: function ts_sendBufferedAmount() { @@ -307,7 +384,10 @@ TCPSocket.prototype = { #endif callListener: function ts_callListener(type, data) { - this.__DOM_IMPL__.dispatchEvent(new this.useWin.TCPSocketEvent(type, {data: data || ""})); + if (!this["on" + type]) + return; + + this["on" + type].call(null, new TCPSocketEvent(type, this, data || "")); }, /* nsITCPSocketInternal methods */ @@ -361,6 +441,39 @@ TCPSocket.prototype = { } }, + 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; @@ -398,68 +511,36 @@ TCPSocket.prototype = { /* end nsITCPSocketInternal methods */ - 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); + initWindowless: function ts_initWindowless() { + try { + return Services.prefs.getBoolPref("dom.mozTCPSocket.enabled"); + } catch (e) { + // no pref means return false + return false; } }, - __init: function(host, port, options) { - if (options.doNotConnect) { - return; - } + init: function ts_init(aWindow) { + if (!this.initWindowless()) + return null; - 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; - } + let principal = aWindow.document.nodePrincipal; + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); - 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 - }, - - initWithGlobal: function(global) { - this.useWin = global; + this.useWin = XPCNativeWrapper.unwrap(aWindow); + this.innerWindowID = util.currentInnerWindowID; + LOG("window init: " + this.innerWindowID); }, observe: function(aSubject, aTopic, aData) { @@ -485,33 +566,68 @@ TCPSocket.prototype = { } }, - initAcceptedParent: function(transport, binaryType, global) { - this.useWin = global; - this._transport = transport; - this._initStream(binaryType); - - // 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); - - // 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 + 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); + + 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); + + 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; + }, + upgradeToSecure: function ts_upgradeToSecure() { if (this._readyState !== kOPEN) { throw new Error("Socket not open."); @@ -535,6 +651,20 @@ TCPSocket.prototype = { } }, + 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; @@ -560,15 +690,14 @@ TCPSocket.prototype = { if (this._binaryType === "arraybuffer") { byteLength = byteLength || data.byteLength; - } else { - byteLength = data.length; } if (this._inChild) { this._socketBridge.sendSend(data, byteOffset, byteLength, ++this._trackingNumber); } - let newBufferedAmount = this.bufferedAmount + byteLength; + let length = this._binaryType === "arraybuffer" ? byteLength : data.length; + let newBufferedAmount = this.bufferedAmount + length; let bufferFull = newBufferedAmount >= BUFFER_SIZE; if (bufferFull) { @@ -593,7 +722,7 @@ TCPSocket.prototype = { new_stream.setData(data, byteOffset, byteLength); } else { new_stream = new StringInputStream(); - new_stream.setData(data, byteLength); + new_stream.setData(data, length); } if (this._waitingForStartTLS) { @@ -836,7 +965,7 @@ TCPSocket.prototype = { // nsIStreamListener (Triggered by _inputStreamPump.asyncRead) onDataAvailable: function ts_onDataAvailable(request, context, inputStream, offset, count) { if (this._binaryType === "arraybuffer") { - let buffer = new (this.useWin.ArrayBuffer)(count); + let buffer = new (this.useWin ? this.useWin.ArrayBuffer : ArrayBuffer)(count); this._inputStreamBinary.readArrayBuffer(count, buffer); this.callListener("data", buffer); } else { @@ -850,19 +979,25 @@ TCPSocket.prototype = { #endif }, - getInternalSocket: function() { - return this.QueryInterface(Ci.nsITCPSocketInternal); - }, - classID: Components.ID("{cda91b22-6472-11e1-aa11-834fec09cd0a}"), - contractID: "@mozilla.org/tcp-socket;1", + 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, + }), + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIDOMTCPSocket, Ci.nsITCPSocketInternal, Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIObserver, Ci.nsISupportsWeakReference ]) -}; +} this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TCPSocket]); diff --git a/dom/network/TCPSocket.manifest b/dom/network/TCPSocket.manifest index 9d65c8a9b524..3a77e37a0b05 100644 --- a/dom/network/TCPSocket.manifest +++ b/dom/network/TCPSocket.manifest @@ -1,6 +1,7 @@ # 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 diff --git a/dom/network/TCPSocketChild.cpp b/dom/network/TCPSocketChild.cpp index 534b2c5ddd26..0d33ee8b5557 100644 --- a/dom/network/TCPSocketChild.cpp +++ b/dom/network/TCPSocketChild.cpp @@ -9,7 +9,7 @@ #include "mozilla/net/NeckoChild.h" #include "mozilla/dom/PBrowserChild.h" #include "mozilla/dom/TabChild.h" -#include "nsITCPSocketInternal.h" +#include "nsIDOMTCPSocket.h" #include "nsJSUtils.h" #include "nsContentUtils.h" #include "jsapi.h" diff --git a/dom/network/TCPSocketParent.cpp b/dom/network/TCPSocketParent.cpp index 361c2b37767c..2ba8361ee9fa 100644 --- a/dom/network/TCPSocketParent.cpp +++ b/dom/network/TCPSocketParent.cpp @@ -6,7 +6,7 @@ #include "jsapi.h" #include "jsfriendapi.h" #include "nsJSUtils.h" -#include "nsITCPSocketInternal.h" +#include "nsIDOMTCPSocket.h" #include "mozilla/unused.h" #include "mozilla/AppProcessChecker.h" #include "mozilla/net/NeckoCommon.h" @@ -350,11 +350,12 @@ TCPSocketParent::SendEvent(const nsAString& aType, JS::Handle aDataVa } NS_IMETHODIMP -TCPSocketParent::SetSocketAndIntermediary(nsITCPSocketInternal *aSocket, - nsITCPSocketIntermediary *aIntermediary) +TCPSocketParent::SetSocketAndIntermediary(nsIDOMTCPSocket *socket, + nsITCPSocketIntermediary *intermediary, + JSContext* cx) { - mSocket = aSocket; - mIntermediary = aIntermediary; + mSocket = socket; + mIntermediary = intermediary; return NS_OK; } diff --git a/dom/network/TCPSocketParent.h b/dom/network/TCPSocketParent.h index cf12ca252109..d6e9ca90de6f 100644 --- a/dom/network/TCPSocketParent.h +++ b/dom/network/TCPSocketParent.h @@ -9,14 +9,13 @@ #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 { @@ -38,7 +37,7 @@ protected: JS::Heap mIntermediaryObj; nsCOMPtr mIntermediary; - nsCOMPtr mSocket; + nsCOMPtr mSocket; nsRefPtr mObserver; bool mIPCOpen; }; diff --git a/dom/network/TCPSocketParentIntermediary.js b/dom/network/TCPSocketParentIntermediary.js index 2061b90d31bd..08e72037e8ca 100644 --- a/dom/network/TCPSocketParentIntermediary.js +++ b/dom/network/TCPSocketParentIntermediary.js @@ -9,9 +9,6 @@ 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() { } @@ -19,7 +16,7 @@ function TCPSocketParentIntermediary() { TCPSocketParentIntermediary.prototype = { _setCallbacks: function(aParentSide, socket) { aParentSide.initJS(this); - this._socket = socket.getInternalSocket(); + this._socket = socket; // Create handlers for every possible callback that attempt to trigger // corresponding callbacks on the child object. @@ -41,10 +38,12 @@ TCPSocketParentIntermediary.prototype = { open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType, aAppId, aInBrowser) { - let socket = new global.mozTCPSocket(aHost, aPort, {useSecureTransport: aUseSSL, binaryType: aBinaryType}); + 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 socketInternal = socket.getInternalSocket(); - socketInternal.initWithGlobal(global); + let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal); socketInternal.setAppId(aAppId); socketInternal.setInBrowser(aInBrowser); @@ -54,23 +53,24 @@ TCPSocketParentIntermediary.prototype = { // Handlers are set to the JS-implemented socket object on the parent side. this._setCallbacks(aParentSide, socket); - return socketInternal; + return socket; }, 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 baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket); + let serverSocket = baseSocket.listen(aLocalPort, { binaryType: aBinaryType }, aBacklog); + if (!serverSocket) + return null; let localPort = serverSocket.localPort; - serverSocket["onconnect"] = function(event) { + serverSocket["onconnect"] = function(socket) { var socketParent = Cc["@mozilla.org/tcp-socket-parent;1"] .createInstance(Ci.nsITCPSocketParent); var intermediary = new TCPSocketParentIntermediary(); - let socketInternal = event.socket.getInternalSocket(); + let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal); socketInternal.setAppId(aAppId); socketInternal.setInBrowser(aInBrowser); socketInternal.setOnUpdateBufferedAmountHandler( @@ -79,11 +79,11 @@ TCPSocketParentIntermediary.prototype = { // 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); + intermediary._setCallbacks(socketParent, 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(socketInternal, intermediary); + socketParent.setSocketAndIntermediary(socket, intermediary); aTCPServerSocketParent.sendCallbackAccept(socketParent); }; @@ -94,7 +94,7 @@ TCPSocketParentIntermediary.prototype = { error.lineNumber, error.columnNumber); }; - return serverSocketInternal; + return serverSocket; }, onRecvSendString: function(aData, aTrackingNumber) { diff --git a/dom/network/TCPSocketUtils.cpp b/dom/network/TCPSocketUtils.cpp deleted file mode 100644 index 1771489f4119..000000000000 --- a/dom/network/TCPSocketUtils.cpp +++ /dev/null @@ -1,31 +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/. */ - -#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 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); -} - -} diff --git a/dom/network/TCPSocketUtils.h b/dom/network/TCPSocketUtils.h deleted file mode 100644 index 7c3c769852b9..000000000000 --- a/dom/network/TCPSocketUtils.h +++ /dev/null @@ -1,17 +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/. */ - -#ifndef TCPSocketUtils_h -#define TCPSocketUtils_h - -#include "js/RootingAPI.h" - -namespace TCPSocketUtils { - -extern bool -SocketEnabled(JSContext* aCx, JS::Handle aGlobal); - -} - -#endif // TCPSocketUtils_h diff --git a/dom/network/interfaces/moz.build b/dom/network/interfaces/moz.build index ed20ea1b828d..a078c7b1be28 100644 --- a/dom/network/interfaces/moz.build +++ b/dom/network/interfaces/moz.build @@ -5,12 +5,12 @@ # 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', - 'nsITCPSocketInternal.idl', 'nsITCPSocketParent.idl', 'nsIUDPSocketChild.idl', ] diff --git a/dom/network/interfaces/nsITCPServerSocketInternal.idl b/dom/network/interfaces/nsIDOMTCPServerSocket.idl similarity index 58% rename from dom/network/interfaces/nsITCPServerSocketInternal.idl rename to dom/network/interfaces/nsIDOMTCPServerSocket.idl index 9c0354dde34c..9f214c973c42 100644 --- a/dom/network/interfaces/nsITCPServerSocketInternal.idl +++ b/dom/network/interfaces/nsIDOMTCPServerSocket.idl @@ -3,8 +3,41 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "domstubs.idl" +#include "nsITCPSocketChild.idl" -interface nsITCPSocketChild; +// 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(); +}; /** * Internal interfaces for use in cross-process server-socket implementation. @@ -15,13 +48,16 @@ interface nsITCPSocketChild; * on the parent and child side for an IPC protocol implementation. */ -[scriptable, uuid(03520F9E-7989-4604-870A-A67DFFD846DC)] -interface nsITCPServerSocketInternal : nsISupports +[scriptable, uuid(b64b1e68-4efa-497c-b0d8-69f067ad5ec8)] +interface nsITCPServerSocketInternal : nsISupports { /** - * Close the server socket. + * Initialization after creating a TCP server socket object. + * + * @param windowVal + * An object to create ArrayBuffer for this window. See Bug 831107. */ - void close(); + void init(in jsval windowVal); /** * Listen on a port @@ -53,9 +89,4 @@ interface nsITCPServerSocketInternal : nsISupports */ 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); }; diff --git a/dom/network/interfaces/nsIDOMTCPSocket.idl b/dom/network/interfaces/nsIDOMTCPSocket.idl new file mode 100644 index 000000000000..1054cdd34238 --- /dev/null +++ b/dom/network/interfaces/nsIDOMTCPSocket.idl @@ -0,0 +1,333 @@ +/* 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" +#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" + +// 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(65f6d2c8-4be6-4695-958d-0735e8935289)] +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); + + /** + * 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; + + /** + * 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 window + // An object to create ArrayBuffer for this window. See Bug 831107. + nsIDOMTCPSocket createAcceptedParent(in nsISocketTransport transport, + in DOMString binaryType, + in nsIDOMWindow window); + + // 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; +}; + diff --git a/dom/network/interfaces/nsITCPServerSocketChild.idl b/dom/network/interfaces/nsITCPServerSocketChild.idl index eb62ade48229..bdaaaa96558c 100644 --- a/dom/network/interfaces/nsITCPServerSocketChild.idl +++ b/dom/network/interfaces/nsITCPServerSocketChild.idl @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "domstubs.idl" +#include "nsIDOMTCPServerSocket.idl" interface nsITCPServerSocketInternal; diff --git a/dom/network/interfaces/nsITCPServerSocketParent.idl b/dom/network/interfaces/nsITCPServerSocketParent.idl index 2aa92fea2f41..bc864ecbf37b 100644 --- a/dom/network/interfaces/nsITCPServerSocketParent.idl +++ b/dom/network/interfaces/nsITCPServerSocketParent.idl @@ -3,10 +3,11 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "domstubs.idl" +#include "nsITCPSocketParent.idl" -interface nsITCPSocketParent; +interface nsIDOMTCPServerSocket; -/** +/** * 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. diff --git a/dom/network/interfaces/nsITCPSocketInternal.idl b/dom/network/interfaces/nsITCPSocketInternal.idl deleted file mode 100644 index 8d242bf0f839..000000000000 --- a/dom/network/interfaces/nsITCPSocketInternal.idl +++ /dev/null @@ -1,133 +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" - -interface nsISocketTransport; -interface nsITCPSocketChild; - -/* - * 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(E79C5F3C-0B54-44B9-B0D7-6930EF9126DC)] -interface nsITCPSocketInternal : nsISupports { - /** - * 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; - - /** - * 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(); - - // 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); - - // Initialize 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. - void initAcceptedParent(in nsISocketTransport transport, - in DOMString binaryType, - in nsISupports global); - - // Initialize 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 global - // An object to create ArrayBuffer for this global. See Bug 831107. - void initAcceptedChild(in nsITCPSocketChild socketChild, - in DOMString binaryType, - in nsISupports global); - - // 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); - - /** - * Called by the parent process when there is no DOM window available. - */ - void initWithGlobal(in nsISupports global); -}; diff --git a/dom/network/interfaces/nsITCPSocketParent.idl b/dom/network/interfaces/nsITCPSocketParent.idl index f6521347f961..d23e90a8e26b 100644 --- a/dom/network/interfaces/nsITCPSocketParent.idl +++ b/dom/network/interfaces/nsITCPSocketParent.idl @@ -4,15 +4,15 @@ #include "domstubs.idl" -interface nsITCPSocketInternal; -interface nsITCPServerSocketInternal; +interface nsIDOMTCPSocket; +interface nsIDOMTCPServerSocket; 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)] +[scriptable, uuid(6f040bf0-6852-11e3-949a-0800200c9a66)] interface nsITCPSocketParent : nsISupports { [implicit_jscontext] void initJS(in jsval intermediary); @@ -40,8 +40,8 @@ interface nsITCPSocketParent : nsISupports // The socket on the parent side. // @param intermediary // Intermediate class object. See nsITCPSocketIntermediary. - void setSocketAndIntermediary(in nsITCPSocketInternal socket, - in nsITCPSocketIntermediary intermediary); + [implicit_jscontext] void setSocketAndIntermediary(in nsIDOMTCPSocket socket, + in nsITCPSocketIntermediary intermediary); // When parent's buffered amount is updated and it wants to inform child to // update the bufferedAmount as well. @@ -68,18 +68,18 @@ interface nsITCPSocketParent : nsISupports [scriptable, uuid(aa9bd46d-26bf-4ba8-9c18-ba02482c02f0)] interface nsITCPSocketIntermediary : nsISupports { // Open the connection to the server with the given parameters - 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); + 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 - nsITCPServerSocketInternal listen(in nsITCPServerSocketParent parent, - in unsigned short port, in unsigned short backlog, - in DOMString binaryType, - in unsigned long appId, - in boolean inBrowser); + nsIDOMTCPServerSocket 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); diff --git a/dom/network/moz.build b/dom/network/moz.build index 8d8e18b87fe8..ee6ac73e747c 100644 --- a/dom/network/moz.build +++ b/dom/network/moz.build @@ -16,10 +16,6 @@ if CONFIG['MOZ_B2G_RIL']: MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] -EXPORTS += [ - 'TCPSocketUtils.h', -] - EXPORTS.mozilla.dom += [ 'UDPSocket.h', ] @@ -42,7 +38,6 @@ UNIFIED_SOURCES += [ 'TCPServerSocketParent.cpp', 'TCPSocketChild.cpp', 'TCPSocketParent.cpp', - 'TCPSocketUtils.cpp', 'UDPSocket.cpp', 'UDPSocketChild.cpp', 'UDPSocketParent.cpp', @@ -86,8 +81,6 @@ IPDL_SOURCES += [ FAIL_ON_WARNINGS = True -LOCAL_INCLUDES += ['/dom/base'] - include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' diff --git a/dom/network/tests/mochitest.ini b/dom/network/tests/mochitest.ini index 4e5c5b81b37e..82dfe82e391c 100644 --- a/dom/network/tests/mochitest.ini +++ b/dom/network/tests/mochitest.ini @@ -9,6 +9,8 @@ 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] diff --git a/dom/network/tests/test_tcpsocket_client_and_server_basics.js b/dom/network/tests/test_tcpsocket_client_and_server_basics.js index 5675f74d40b2..30f83dfdd222 100644 --- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js +++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js @@ -127,7 +127,7 @@ function waitForConnection(listeningServer) { // 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(ev) { + listeningServer.onconnect = function(socket) { // Clobber the listener to get upset if it receives any more connections // after this. listeningServer.onconnect = function() { @@ -135,14 +135,10 @@ function waitForConnection(listeningServer) { }; ok(true, 'Listening server accepted socket'); resolve({ - socket: ev.socket, - queue: listenForEventsOnSocket(ev.socket, 'server') + socket: socket, + queue: listenForEventsOnSocket(socket, 'server') }); }; - listeningServer.onerror = function(ev) { - ok(false, 'Received an error when not expecting one.'); - reject(); - }; }); } @@ -175,16 +171,17 @@ function* test_basics() { // 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 listeningServer = TCPSocket.listen(serverPort, + { binaryType: 'arraybuffer' }, + SERVER_BACKLOG); let connectedPromise = waitForConnection(listeningServer); // -- Open a connection to the server - let clientSocket = new mozTCPSocket('127.0.0.1', serverPort, - { binaryType: 'arraybuffer' }); + let clientSocket = TCPSocket.open('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); let clientQueue = listenForEventsOnSocket(clientSocket, 'client'); // (the client connects) @@ -290,8 +287,8 @@ function* test_basics() { // -- Re-establish connection connectedPromise = waitForConnection(listeningServer); - clientSocket = new mozTCPSocket('127.0.0.1', serverPort, - { binaryType: 'arraybuffer' }); + clientSocket = TCPSocket.open('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); clientQueue = listenForEventsOnSocket(clientSocket, 'client'); is((yield clientQueue.waitForEvent()).type, 'open', 'got open event'); @@ -317,8 +314,8 @@ function* test_basics() { // -- Re-establish connection connectedPromise = waitForConnection(listeningServer); - clientSocket = new mozTCPSocket('127.0.0.1', serverPort, - { binaryType: 'arraybuffer' }); + clientSocket = TCPSocket.open('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); clientQueue = listenForEventsOnSocket(clientSocket, 'client'); is((yield clientQueue.waitForEvent()).type, 'open', 'got open event'); @@ -346,7 +343,7 @@ function* test_basics() { 'server received/client sent'); // And a close. is((yield serverQueue.waitForEvent()).type, 'close', - 'The close event should fire after a large send that returned true.'); + 'The drain event should fire after a large send that returned true.'); // -- Close the listening server (and try to connect) @@ -355,8 +352,8 @@ function* test_basics() { listeningServer.close(); // - try and connect, get an error - clientSocket = new mozTCPSocket('127.0.0.1', serverPort, - { binaryType: 'arraybuffer' }); + clientSocket = TCPSocket.open('127.0.0.1', serverPort, + { binaryType: 'arraybuffer' }); clientQueue = listenForEventsOnSocket(clientSocket, 'client'); is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect'); is(clientSocket.readyState, 'closed', diff --git a/dom/network/tests/test_tcpsocket_default_permissions.html b/dom/network/tests/test_tcpsocket_default_permissions.html index 4fe272df3732..8c0f42e32c0f 100644 --- a/dom/network/tests/test_tcpsocket_default_permissions.html +++ b/dom/network/tests/test_tcpsocket_default_permissions.html @@ -14,7 +14,12 @@ /** Test to ensure TCPSocket permission is disabled by default **/ -ok(!("mozTCPSocket" in window), "mozTCPSocket should not exist 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"); +} diff --git a/dom/network/tests/test_tcpsocket_enabled_no_perm.html b/dom/network/tests/test_tcpsocket_enabled_no_perm.html new file mode 100644 index 000000000000..a028b48d60cc --- /dev/null +++ b/dom/network/tests/test_tcpsocket_enabled_no_perm.html @@ -0,0 +1,35 @@ + + + + Test to ensure TCPSocket permission enabled and no tcp-socket perm does not allow open + + + + +

+ +
+
+
+ + diff --git a/dom/network/tests/test_tcpsocket_enabled_with_perm.html b/dom/network/tests/test_tcpsocket_enabled_with_perm.html index 5def35f1e5d6..4490743e4c29 100644 --- a/dom/network/tests/test_tcpsocket_enabled_with_perm.html +++ b/dom/network/tests/test_tcpsocket_enabled_with_perm.html @@ -12,16 +12,16 @@