2015-03-20 00:12:58 +03:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
2015-10-07 15:03:21 +03:00
|
|
|
var {Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
2015-03-20 00:12:58 +03:00
|
|
|
|
2015-10-07 15:03:21 +03:00
|
|
|
var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
|
2015-03-20 00:12:58 +03:00
|
|
|
const ServerSocket = CC("@mozilla.org/network/server-socket;1", "nsIServerSocket", "initSpecialConnection");
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/Log.jsm");
|
2016-01-19 01:05:30 +03:00
|
|
|
Cu.import("resource://gre/modules/Preferences.jsm");
|
2015-03-20 00:12:58 +03:00
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
|
|
|
|
Cu.import("chrome://marionette/content/dispatcher.js");
|
|
|
|
Cu.import("chrome://marionette/content/driver.js");
|
2016-02-03 22:14:10 +03:00
|
|
|
Cu.import("chrome://marionette/content/element.js");
|
2015-03-24 00:32:03 +03:00
|
|
|
Cu.import("chrome://marionette/content/simpletest.js");
|
2015-03-20 00:12:58 +03:00
|
|
|
|
|
|
|
// Bug 1083711: Load transport.js as an SDK module instead of subscript
|
2015-10-14 02:18:43 +03:00
|
|
|
loader.loadSubScript("resource://devtools/shared/transport/transport.js");
|
2015-03-20 00:12:58 +03:00
|
|
|
|
|
|
|
const logger = Log.repository.getLogger("Marionette");
|
|
|
|
|
|
|
|
this.EXPORTED_SYMBOLS = ["MarionetteServer"];
|
2016-01-19 01:05:30 +03:00
|
|
|
|
2015-03-20 00:12:58 +03:00
|
|
|
const CONTENT_LISTENER_PREF = "marionette.contentListener";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Bootstraps Marionette and handles incoming client connections.
|
|
|
|
*
|
|
|
|
* Once started, it opens a TCP socket sporting the debugger transport
|
|
|
|
* protocol on the provided port. For every new client a Dispatcher is
|
|
|
|
* created.
|
|
|
|
*
|
|
|
|
* @param {number} port
|
|
|
|
* Port for server to listen to.
|
|
|
|
* @param {boolean} forceLocal
|
|
|
|
* Listen only to connections from loopback if true. If false,
|
|
|
|
* accept all connections.
|
|
|
|
*/
|
2016-12-04 14:42:52 +03:00
|
|
|
this.MarionetteServer = function (port, forceLocal) {
|
2015-03-20 00:12:58 +03:00
|
|
|
this.port = port;
|
|
|
|
this.forceLocal = forceLocal;
|
|
|
|
this.conns = {};
|
|
|
|
this.nextConnId = 0;
|
|
|
|
this.alive = false;
|
2016-10-17 14:19:19 +03:00
|
|
|
this._acceptConnections = false;
|
2015-03-20 00:12:58 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-05-24 12:22:54 +03:00
|
|
|
* Function produces a GeckoDriver.
|
2015-03-20 00:12:58 +03:00
|
|
|
*
|
2016-06-28 17:26:49 +03:00
|
|
|
* Determines application nameto initialise the driver with.
|
2015-03-20 00:12:58 +03:00
|
|
|
*
|
|
|
|
* @return {GeckoDriver}
|
|
|
|
* A driver instance.
|
|
|
|
*/
|
2016-05-24 12:22:54 +03:00
|
|
|
MarionetteServer.prototype.driverFactory = function() {
|
2016-01-19 01:05:30 +03:00
|
|
|
Preferences.set(CONTENT_LISTENER_PREF, false);
|
2017-03-06 19:45:35 +03:00
|
|
|
return new GeckoDriver(Services.appinfo.name, this);
|
2015-03-20 00:12:58 +03:00
|
|
|
};
|
|
|
|
|
2016-12-04 14:42:52 +03:00
|
|
|
MarionetteServer.prototype.__defineSetter__("acceptConnections", function (value) {
|
2016-10-17 14:19:19 +03:00
|
|
|
if (!value) {
|
|
|
|
logger.info("New connections will no longer be accepted");
|
|
|
|
} else {
|
|
|
|
logger.info("New connections are accepted again");
|
|
|
|
}
|
|
|
|
|
|
|
|
this._acceptConnections = value;
|
|
|
|
});
|
|
|
|
|
2015-03-20 00:12:58 +03:00
|
|
|
MarionetteServer.prototype.start = function() {
|
|
|
|
if (this.alive) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let flags = Ci.nsIServerSocket.KeepWhenOffline;
|
|
|
|
if (this.forceLocal) {
|
|
|
|
flags |= Ci.nsIServerSocket.LoopbackOnly;
|
|
|
|
}
|
2016-06-22 00:37:40 +03:00
|
|
|
this.listener = new ServerSocket(this.port, flags, 1);
|
2015-03-20 00:12:58 +03:00
|
|
|
this.listener.asyncListen(this);
|
|
|
|
this.alive = true;
|
2016-10-17 14:19:19 +03:00
|
|
|
this._acceptConnections = true;
|
2015-03-20 00:12:58 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
MarionetteServer.prototype.stop = function() {
|
|
|
|
if (!this.alive) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.closeListener();
|
|
|
|
this.alive = false;
|
2016-10-17 14:19:19 +03:00
|
|
|
this._acceptConnections = false;
|
2015-03-20 00:12:58 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
MarionetteServer.prototype.closeListener = function() {
|
|
|
|
this.listener.close();
|
|
|
|
this.listener = null;
|
|
|
|
};
|
|
|
|
|
2016-12-04 14:42:52 +03:00
|
|
|
MarionetteServer.prototype.onSocketAccepted = function (
|
2015-03-20 00:12:58 +03:00
|
|
|
serverSocket, clientSocket) {
|
2016-10-17 14:19:19 +03:00
|
|
|
if (!this._acceptConnections) {
|
|
|
|
logger.warn("New connections are currently not accepted");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-20 00:12:58 +03:00
|
|
|
let input = clientSocket.openInputStream(0, 0, 0);
|
|
|
|
let output = clientSocket.openOutputStream(0, 0, 0);
|
|
|
|
let transport = new DebuggerTransport(input, output);
|
|
|
|
let connId = "conn" + this.nextConnId++;
|
|
|
|
|
2015-09-26 19:12:01 +03:00
|
|
|
let dispatcher = new Dispatcher(connId, transport, this.driverFactory.bind(this));
|
2015-03-20 00:12:58 +03:00
|
|
|
dispatcher.onclose = this.onConnectionClosed.bind(this);
|
|
|
|
this.conns[connId] = dispatcher;
|
|
|
|
|
2016-01-18 22:09:31 +03:00
|
|
|
logger.debug(`Accepted connection ${connId} from ${clientSocket.host}:${clientSocket.port}`);
|
2015-03-20 00:12:58 +03:00
|
|
|
dispatcher.sayHello();
|
|
|
|
transport.ready();
|
|
|
|
};
|
|
|
|
|
2016-12-04 14:42:52 +03:00
|
|
|
MarionetteServer.prototype.onConnectionClosed = function (conn) {
|
2015-09-26 19:12:01 +03:00
|
|
|
let id = conn.connId;
|
2015-03-20 00:12:58 +03:00
|
|
|
delete this.conns[id];
|
2016-01-18 22:09:31 +03:00
|
|
|
logger.debug(`Closed connection ${id}`);
|
2015-03-20 00:12:58 +03:00
|
|
|
};
|
|
|
|
|