server.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  3. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. var {Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu} = Components;
  6. var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
  7. const ServerSocket = CC("@mozilla.org/network/server-socket;1", "nsIServerSocket", "initSpecialConnection");
  8. Cu.import("resource://gre/modules/Log.jsm");
  9. Cu.import("resource://gre/modules/Preferences.jsm");
  10. Cu.import("resource://gre/modules/Services.jsm");
  11. Cu.import("chrome://marionette/content/dispatcher.js");
  12. Cu.import("chrome://marionette/content/driver.js");
  13. Cu.import("chrome://marionette/content/element.js");
  14. Cu.import("chrome://marionette/content/simpletest.js");
  15. // Bug 1083711: Load transport.js as an SDK module instead of subscript
  16. loader.loadSubScript("resource://devtools/shared/transport/transport.js");
  17. const logger = Log.repository.getLogger("Marionette");
  18. this.EXPORTED_SYMBOLS = ["MarionetteServer"];
  19. const CONTENT_LISTENER_PREF = "marionette.contentListener";
  20. const MANAGE_OFFLINE_STATUS_PREF = "network.gonk.manage-offline-status";
  21. /**
  22. * Bootstraps Marionette and handles incoming client connections.
  23. *
  24. * Once started, it opens a TCP socket sporting the debugger transport
  25. * protocol on the provided port. For every new client a Dispatcher is
  26. * created.
  27. *
  28. * @param {number} port
  29. * Port for server to listen to.
  30. * @param {boolean} forceLocal
  31. * Listen only to connections from loopback if true. If false,
  32. * accept all connections.
  33. */
  34. this.MarionetteServer = function (port, forceLocal) {
  35. this.port = port;
  36. this.forceLocal = forceLocal;
  37. this.conns = {};
  38. this.nextConnId = 0;
  39. this.alive = false;
  40. this._acceptConnections = false;
  41. };
  42. /**
  43. * Function produces a GeckoDriver.
  44. *
  45. * Determines application nameto initialise the driver with.
  46. *
  47. * @return {GeckoDriver}
  48. * A driver instance.
  49. */
  50. MarionetteServer.prototype.driverFactory = function() {
  51. let appName = isMulet() ? "B2G" : Services.appinfo.name;
  52. let bypassOffline = false;
  53. Preferences.set(CONTENT_LISTENER_PREF, false);
  54. if (bypassOffline) {
  55. logger.debug("Bypassing offline status");
  56. Preferences.set(MANAGE_OFFLINE_STATUS_PREF, false);
  57. Services.io.manageOfflineStatus = false;
  58. Services.io.offline = false;
  59. }
  60. return new GeckoDriver(appName, this);
  61. };
  62. MarionetteServer.prototype.__defineSetter__("acceptConnections", function (value) {
  63. if (!value) {
  64. logger.info("New connections will no longer be accepted");
  65. } else {
  66. logger.info("New connections are accepted again");
  67. }
  68. this._acceptConnections = value;
  69. });
  70. MarionetteServer.prototype.start = function() {
  71. if (this.alive) {
  72. return;
  73. }
  74. let flags = Ci.nsIServerSocket.KeepWhenOffline;
  75. if (this.forceLocal) {
  76. flags |= Ci.nsIServerSocket.LoopbackOnly;
  77. }
  78. this.listener = new ServerSocket(this.port, flags, 1);
  79. this.listener.asyncListen(this);
  80. this.alive = true;
  81. this._acceptConnections = true;
  82. };
  83. MarionetteServer.prototype.stop = function() {
  84. if (!this.alive) {
  85. return;
  86. }
  87. this.closeListener();
  88. this.alive = false;
  89. this._acceptConnections = false;
  90. };
  91. MarionetteServer.prototype.closeListener = function() {
  92. this.listener.close();
  93. this.listener = null;
  94. };
  95. MarionetteServer.prototype.onSocketAccepted = function (
  96. serverSocket, clientSocket) {
  97. if (!this._acceptConnections) {
  98. logger.warn("New connections are currently not accepted");
  99. return;
  100. }
  101. let input = clientSocket.openInputStream(0, 0, 0);
  102. let output = clientSocket.openOutputStream(0, 0, 0);
  103. let transport = new DebuggerTransport(input, output);
  104. let connId = "conn" + this.nextConnId++;
  105. let dispatcher = new Dispatcher(connId, transport, this.driverFactory.bind(this));
  106. dispatcher.onclose = this.onConnectionClosed.bind(this);
  107. this.conns[connId] = dispatcher;
  108. logger.debug(`Accepted connection ${connId} from ${clientSocket.host}:${clientSocket.port}`);
  109. dispatcher.sayHello();
  110. transport.ready();
  111. };
  112. MarionetteServer.prototype.onConnectionClosed = function (conn) {
  113. let id = conn.connId;
  114. delete this.conns[id];
  115. logger.debug(`Closed connection ${id}`);
  116. };
  117. function isMulet() {
  118. return Preferences.get("b2g.is_mulet", false);
  119. }