diff --git a/toolkit/devtools/client/connection-manager.js b/toolkit/devtools/client/connection-manager.js index fe636449893d..f94a9f79e2a1 100644 --- a/toolkit/devtools/client/connection-manager.js +++ b/toolkit/devtools/client/connection-manager.js @@ -6,7 +6,7 @@ "use strict"; -const {Cu} = require("chrome"); +const {Cc, Ci, Cu} = require("chrome"); const {setTimeout, clearTimeout} = require('sdk/timers'); const EventEmitter = require("devtools/shared/event-emitter"); @@ -25,6 +25,7 @@ Cu.import("resource://gre/modules/devtools/dbg-server.jsm"); * Methods: * ⬩ Connection createConnection(host, port) * ⬩ void destroyConnection(connection) + * ⬩ Number getFreeTCPPort() * * Properties: * ⬩ Array connections @@ -46,6 +47,7 @@ Cu.import("resource://gre/modules/devtools/dbg-server.jsm"); * ⬩ port Port * ⬩ logs Current logs. "newlog" event notifies new available logs * ⬩ store Reference to a local data store (see below) + * ⬩ keepConnecting Should the connection keep trying connecting * ⬩ status Connection status: * Connection.Status.CONNECTED * Connection.Status.DISCONNECTED @@ -86,6 +88,14 @@ let ConnectionManager = { get connections() { return [c for (c of this._connections)]; }, + getFreeTCPPort: function () { + let serv = Cc['@mozilla.org/network/server-socket;1'] + .createInstance(Ci.nsIServerSocket); + serv.init(-1, true, -1); + let port = serv.port; + serv.close(); + return port; + }, } EventEmitter.decorate(ConnectionManager); @@ -101,6 +111,7 @@ function Connection(host, port) { this._onDisconnected = this._onDisconnected.bind(this); this._onConnected = this._onConnected.bind(this); this._onTimeout = this._onTimeout.bind(this); + this.keepConnecting = false; } Connection.Status = { @@ -180,15 +191,7 @@ Connection.prototype = { let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout"); this._timeoutID = setTimeout(this._onTimeout, delay); - let transport; - if (!this._host) { - transport = DebuggerServer.connectPipe(); - } else { - transport = debuggerSocketConnect(this._host, this._port); - } - this._client = new DebuggerClient(transport); - this._client.addOneTimeListener("closed", this._onDisconnected); - this._client.connect(this._onConnected); + this._clientConnect(); } else { let msg = "Can't connect. Client is not fully disconnected"; this.log(msg); @@ -199,6 +202,7 @@ Connection.prototype = { destroy: function() { this.log("killing connection"); clearTimeout(this._timeoutID); + this.keepConnecting = false; if (this._client) { this._client.close(); this._client = null; @@ -206,6 +210,18 @@ Connection.prototype = { this._setStatus(Connection.Status.DESTROYED); }, + _clientConnect: function () { + let transport; + if (!this._host) { + transport = DebuggerServer.connectPipe(); + } else { + transport = debuggerSocketConnect(this._host, this._port); + } + this._client = new DebuggerClient(transport); + this._client.addOneTimeListener("closed", this._onDisconnected); + this._client.connect(this._onConnected); + }, + get status() { return this._status }, @@ -219,6 +235,13 @@ Connection.prototype = { }, _onDisconnected: function() { + this._client = null; + + if (this._status == Connection.Status.CONNECTING && this.keepConnecting) { + setTimeout(() => this._clientConnect(), 0); + return; + } + clearTimeout(this._timeoutID); switch (this.status) { case Connection.Status.CONNECTED: @@ -230,7 +253,6 @@ Connection.prototype = { default: this.log("disconnected"); } - this._client = null; this._setStatus(Connection.Status.DISCONNECTED); }, @@ -242,7 +264,7 @@ Connection.prototype = { _onTimeout: function() { this.log("connection timeout"); - this.emit(Connection.Events.TIMEOUT, str); + this.emit(Connection.Events.TIMEOUT); this.disconnect(); }, } diff --git a/toolkit/devtools/server/tests/mochitest/test_connection-manager.html b/toolkit/devtools/server/tests/mochitest/test_connection-manager.html index 263352789f63..c747f0c9af36 100644 --- a/toolkit/devtools/server/tests/mochitest/test_connection-manager.html +++ b/toolkit/devtools/server/tests/mochitest/test_connection-manager.html @@ -20,6 +20,7 @@ window.onload = function() { Cu.import("resource://gre/modules/devtools/dbg-server.jsm"); Cu.import("resource://gre/modules/devtools/Loader.jsm"); + Cu.import("resource://gre/modules/Services.jsm"); DebuggerServer.init(function () { return true; }); DebuggerServer.addBrowserActors(); @@ -43,7 +44,7 @@ window.onload = function() { var c = c2; - var eventsRef = "connecting connected disconnecting disconnected host-changed disconnected destroyed"; + var eventsRef = "connecting connected disconnecting disconnected host-changed disconnected timeout destroyed"; var events = []; var s = Connection.Status; @@ -70,7 +71,7 @@ window.onload = function() { function testError() { c.once(Connection.Events.DISCONNECTED, function(e) { events.push(e); - ConnectionManager.destroyConnection(c); + testKeepConnecting(); }); c.once(Connection.Events.HOST_CHANGED, function(e) { events.push(e); @@ -80,6 +81,24 @@ window.onload = function() { c.host = "localhost"; } + function testKeepConnecting() { + // ensure that keepConnecting keep trying connecting + // until the connection attempts timeout + var originalTimeout = Services.prefs.getIntPref("devtools.debugger.remote-timeout"); + Services.prefs.setIntPref("devtools.debugger.remote-timeout", 1000); + c.once("timeout", function (e) { + events.push(e); + Services.prefs.setIntPref("devtools.debugger.remote-timeout", originalTimeout); + ConnectionManager.destroyConnection(c); + }); + c.keepConnecting = true; + var port = ConnectionManager.getFreeTCPPort(); + ok(parseInt(port), "Free TCP port looks like a port number"); + c.port = port; + c.host = "locahost"; + c.connect(); + } + function finish() { is(events.join(" "), eventsRef, "Events received in the right order"); DebuggerServer.destroy();