summaryrefslogtreecommitdiffstats
path: root/testing/marionette/server.js
blob: 0d8a7bf23d0ca5e56f5f8704e88511bb5af79deb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* 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";

var {Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu} = Components;

var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
const ServerSocket = CC("@mozilla.org/network/server-socket;1", "nsIServerSocket", "initSpecialConnection");

Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");

Cu.import("chrome://marionette/content/dispatcher.js");
Cu.import("chrome://marionette/content/driver.js");
Cu.import("chrome://marionette/content/element.js");
Cu.import("chrome://marionette/content/simpletest.js");

// Bug 1083711: Load transport.js as an SDK module instead of subscript
loader.loadSubScript("resource://devtools/shared/transport/transport.js");

const logger = Log.repository.getLogger("Marionette");

this.EXPORTED_SYMBOLS = ["MarionetteServer"];

const CONTENT_LISTENER_PREF = "marionette.contentListener";
const MANAGE_OFFLINE_STATUS_PREF = "network.gonk.manage-offline-status";

/**
 * 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.
 */
this.MarionetteServer = function (port, forceLocal) {
  this.port = port;
  this.forceLocal = forceLocal;
  this.conns = {};
  this.nextConnId = 0;
  this.alive = false;
  this._acceptConnections = false;
};

/**
 * Function produces a GeckoDriver.
 *
 * Determines application nameto initialise the driver with.
 *
 * @return {GeckoDriver}
 *     A driver instance.
 */
MarionetteServer.prototype.driverFactory = function() {
  let appName = isMulet() ? "B2G" : Services.appinfo.name;
  let bypassOffline = false;

  Preferences.set(CONTENT_LISTENER_PREF, false);

  if (bypassOffline) {
      logger.debug("Bypassing offline status");
      Preferences.set(MANAGE_OFFLINE_STATUS_PREF, false);
      Services.io.manageOfflineStatus = false;
      Services.io.offline = false;
  }

  return new GeckoDriver(appName, this);
};

MarionetteServer.prototype.__defineSetter__("acceptConnections", function (value) {
  if (!value) {
    logger.info("New connections will no longer be accepted");
  } else {
    logger.info("New connections are accepted again");
  }

  this._acceptConnections = value;
});

MarionetteServer.prototype.start = function() {
  if (this.alive) {
    return;
  }
  let flags = Ci.nsIServerSocket.KeepWhenOffline;
  if (this.forceLocal) {
    flags |= Ci.nsIServerSocket.LoopbackOnly;
  }
  this.listener = new ServerSocket(this.port, flags, 1);
  this.listener.asyncListen(this);
  this.alive = true;
  this._acceptConnections = true;
};

MarionetteServer.prototype.stop = function() {
  if (!this.alive) {
    return;
  }
  this.closeListener();
  this.alive = false;
  this._acceptConnections = false;
};

MarionetteServer.prototype.closeListener = function() {
  this.listener.close();
  this.listener = null;
};

MarionetteServer.prototype.onSocketAccepted = function (
    serverSocket, clientSocket) {
  if (!this._acceptConnections) {
    logger.warn("New connections are currently not accepted");
    return;
  }

  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++;

  let dispatcher = new Dispatcher(connId, transport, this.driverFactory.bind(this));
  dispatcher.onclose = this.onConnectionClosed.bind(this);
  this.conns[connId] = dispatcher;

  logger.debug(`Accepted connection ${connId} from ${clientSocket.host}:${clientSocket.port}`);
  dispatcher.sayHello();
  transport.ready();
};

MarionetteServer.prototype.onConnectionClosed = function (conn) {
  let id = conn.connId;
  delete this.conns[id];
  logger.debug(`Closed connection ${id}`);
};

function isMulet() {
  return Preferences.get("b2g.is_mulet", false);
}