Bug 797529 - Remove dependence on debugger server, r=jgriffin
☠☠ backed out by a5c70a04bdcc ☠ ☠
authorWilliam Lachance <wlachance@mozilla.com>
Tue, 14 May 2013 16:54:07 -0400
changeset 134290 36b68f9e4e042293ff444b2b853edd299d141f34
parent 134289 adc90bff145fd852a2633a941193e77d11f0e960
child 134291 bbe73080e0e85414e650bc43a40ba87eb97c2c95
push id29149
push userjgriffin@mozilla.com
push dateThu, 06 Jun 2013 23:14:52 +0000
treeherdermozilla-inbound@36b68f9e4e04 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgriffin
bugs797529
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 797529 - Remove dependence on debugger server, r=jgriffin
testing/marionette/components/marionettecomponent.js
testing/marionette/jar.mn
testing/marionette/marionette-actors.js
testing/marionette/marionette-listener.js
testing/marionette/marionette-log-obj.js
testing/marionette/marionette-server.js
testing/marionette/marionette-simpletest.js
--- a/testing/marionette/components/marionettecomponent.js
+++ b/testing/marionette/components/marionettecomponent.js
@@ -1,37 +1,41 @@
 /* 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/. */
 
-const {Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu} = Components;
+this.CC = Components.Constructor;
+this.Cc = Components.classes;
+this.Ci = Components.interfaces;
+this.Cu = Components.utils;
 
 const MARIONETTE_CONTRACTID = "@mozilla.org/marionette;1";
 const MARIONETTE_CID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}");
-const DEBUGGER_ENABLED_PREF = 'devtools.debugger.remote-enabled';
 const MARIONETTE_ENABLED_PREF = 'marionette.defaultPrefs.enabled';
-const DEBUGGER_FORCELOCAL_PREF = 'devtools.debugger.force-local';
 const MARIONETTE_FORCELOCAL_PREF = 'marionette.force-local';
 
-const ServerSocket = CC("@mozilla.org/network/server-socket;1",
-                        "nsIServerSocket",
-                        "initSpecialConnection");
+this.ServerSocket = CC("@mozilla.org/network/server-socket;1",
+                       "nsIServerSocket",
+                       "initSpecialConnection");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/services-common/log4moz.js");
 
+let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
+               .getService(Ci.mozIJSSubScriptLoader);
+
 function MarionetteComponent() {
   this._loaded = false;
   // set up the logger
   this.logger = Log4Moz.repository.getLogger("Marionette");
-  this.logger.level = Log4Moz.Level["INFO"];
+  this.logger.level = Log4Moz.Level["Info"];
   let logf = FileUtils.getFile('ProfD', ['marionette.log']);
-  
+
   let formatter = new Log4Moz.BasicFormatter();
   this.logger.addAppender(new Log4Moz.RotatingFileAppender(logf, formatter));
   this.logger.addAppender(new Log4Moz.DumpAppender(formatter));
   this.logger.info("MarionetteComponent loaded");
 }
 
 MarionetteComponent.prototype = {
   classDescription: "Marionette component",
@@ -69,32 +73,27 @@ MarionetteComponent.prototype = {
         else {
           this.logger.info("marionette not enabled");
         }
         break;
       case "final-ui-startup":
         this.logger.info("marionette initializing at " + aTopic);
         observerService.removeObserver(this, aTopic);
 
-        try {
-          this.original_forcelocal = Services.prefs.getBoolPref(DEBUGGER_FORCELOCAL_PREF);
-        }
-        catch(e) {}
-
         let marionette_forcelocal = this.appName == 'B2G' ? false : true;
         try {
           marionette_forcelocal = Services.prefs.getBoolPref(MARIONETTE_FORCELOCAL_PREF);
         }
         catch(e) {}
-        Services.prefs.setBoolPref(DEBUGGER_FORCELOCAL_PREF, marionette_forcelocal);
+        Services.prefs.setBoolPref(MARIONETTE_FORCELOCAL_PREF, marionette_forcelocal);
 
         if (!marionette_forcelocal) {
           // See bug 800138.  Because the first socket that opens with
           // force-local=false fails, we open a dummy socket that will fail.
-	  // keepWhenOffline=true so that it still work when offline (local).
+          // keepWhenOffline=true so that it still work when offline (local).
           // This allows the following attempt by Marionette to open a socket
           // to succeed.
           let insaneSacrificialGoat = new ServerSocket(666, Ci.nsIServerSocket.KeepWhenOffline, 4);
           insaneSacrificialGoat.asyncListen(this);
         }
 
         this.init();
         break;
@@ -111,45 +110,27 @@ MarionetteComponent.prototype = {
       let port;
       try {
         port = Services.prefs.getIntPref('marionette.defaultPrefs.port');
       }
       catch(e) {
         port = 2828;
       }
       try {
-        Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
-        DebuggerServer.addActors('chrome://marionette/content/marionette-actors.js');
-        // This pref is required for the remote debugger to open a socket,
-        // so force it to true.  See bug 761252.
-
-        let original = false;
-        try {
-          original = Services.prefs.getBoolPref(DEBUGGER_ENABLED_PREF);
-        }
-        catch(e) { }
-        Services.prefs.setBoolPref(DEBUGGER_ENABLED_PREF, true);
-
-        // Always allow remote connections.
-        DebuggerServer.initTransport(function () { return true; });
-        DebuggerServer.openListener(port);
-
-        Services.prefs.setBoolPref(DEBUGGER_ENABLED_PREF, original);
-        if (this.original_forcelocal != null) {
-          Services.prefs.setBoolPref(DEBUGGER_FORCELOCAL_PREF,
-                                     this.original_forcelocal);
-        }
-        this.logger.info("marionette listener opened");
+        loader.loadSubScript("chrome://marionette/content/marionette-server.js");
+        let forceLocal = Services.prefs.getBoolPref(MARIONETTE_FORCELOCAL_PREF);
+        this._marionetteServer = new MarionetteServer(port, forceLocal);
+        this.logger.info("Marionette server ready");
       }
       catch(e) {
         this.logger.error('exception: ' + e.name + ', ' + e.message);
       }
     }
   },
 
   uninit: function mc_uninit() {
-    DebuggerServer.closeListener();
+    this._marionetteServer.closeListener();
     this._loaded = false;
   },
 
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MarionetteComponent]);
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -1,15 +1,15 @@
 # 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/.
 
 marionette.jar:
 % content marionette %content/
-  content/marionette-actors.js      (marionette-actors.js)
+  content/marionette-server.js      (marionette-server.js)
   content/marionette-listener.js    (marionette-listener.js)
   content/marionette-elements.js    (marionette-elements.js)
   content/marionette-sendkeys.js    (marionette-sendkeys.js)
   content/marionette-log-obj.js     (marionette-log-obj.js)
   content/marionette-simpletest.js  (marionette-simpletest.js)
   content/EventUtils.js  (EventUtils.js)
   content/ChromeUtils.js  (ChromeUtils.js)
 #ifdef ENABLE_TESTS
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -62,16 +62,21 @@ let nextTouchId = 1000;
 //Keep track of active Touches
 let touchIds = {};
 // last touch for each fingerId
 let multiLast = {};
 let lastCoordinates = null;
 let isTap = false;
 // whether to send mouse event
 let mouseEventsOnly = false;
+
+Cu.import("resource://gre/modules/services-common/log4moz.js");
+let logger = Log4Moz.repository.getLogger("Marionette");
+logger.info("loaded marionette-listener.js");
+
 /**
  * Called when listener is first started up. 
  * The listener sends its unique window ID and its current URI to the actor.
  * If the actor returns an ID, we start the listeners. Otherwise, nothing happens.
  */
 function registerSelf() {
   let msg = {value: winUtil.outerWindowID, href: content.location.href};
   let register = sendSyncMessage("Marionette:register", msg);
--- a/testing/marionette/marionette-log-obj.js
+++ b/testing/marionette/marionette-log-obj.js
@@ -1,13 +1,13 @@
 /* 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/. */
 
-function MarionetteLogObj() {
+this.MarionetteLogObj = function MarionetteLogObj() {
   this.logs = [];
 }
 MarionetteLogObj.prototype = {
   /**
    * Log message. Accepts user defined log-level.
    * @param msg String
    *        The message to be logged
    * @param level String
rename from testing/marionette/marionette-actors.js
rename to testing/marionette/marionette-server.js
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-server.js
@@ -1,21 +1,20 @@
 /* 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";
 
-/**
- * Gecko-specific actors.
- */
-
 const FRAME_SCRIPT = "chrome://marionette/content/marionette-listener.js";
 
-let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+// import logger
+Cu.import("resource://gre/modules/services-common/log4moz.js");
+let logger = Log4Moz.repository.getLogger("Marionette");
+logger.info('marionette-server.js loaded');
 
 let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                .getService(Ci.mozIJSSubScriptLoader);
 loader.loadSubScript("chrome://marionette/content/marionette-simpletest.js");
 loader.loadSubScript("chrome://marionette/content/marionette-log-obj.js");
 Cu.import("chrome://marionette/content/marionette-elements.js");
 let utils = {};
 loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
@@ -30,20 +29,21 @@ specialpowers.specialPowersObserver.init
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");  
 
 Services.prefs.setBoolPref("marionette.contentListener", false);
 let appName = Services.appinfo.name;
 
-// import logger
-Cu.import("resource://gre/modules/services-common/log4moz.js");
-let logger = Log4Moz.repository.getLogger("Marionette");
-logger.info('marionette-actors.js loaded');
+// dumpn needed/used by dbg-transport.js
+this.dumpn = function dumpn(str) {
+  logger.trace(str);
+}
+loader.loadSubScript("resource://gre/modules/devtools/server/transport.js");
 
 let bypassOffline = false;
 
 try {
   XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
     Cu.import("resource://gre/modules/systemlibs.js");
     return libcutils;
   });
@@ -60,89 +60,25 @@ catch(e) {}
 if (bypassOffline) {
   logger.info("Bypassing offline status.");
   Services.prefs.setBoolPref("network.gonk.manage-offline-status", false);
   Services.io.manageOfflineStatus = false;
   Services.io.offline = false;
 }
 
 // This is used to prevent newSession from returning before the telephony
-// API's are ready; see bug 792647.  This assumes that marionette-actors.js
+// API's are ready; see bug 792647.  This assumes that marionette-server.js
 // will be loaded before the 'system-message-listener-ready' message
 // is fired.  If this stops being true, this approach will have to change.
 let systemMessageListenerReady = false;
 Services.obs.addObserver(function() {
   systemMessageListenerReady = true;
 }, "system-message-listener-ready", false);
 
 /**
- * Creates the root actor once a connection is established
- */
-
-function createRootActor(aConnection)
-{
-  return new MarionetteRootActor(aConnection);
-}
-
-/**
- * Root actor for Marionette. Add any future actors to its actor pool.
- * Implements methods needed by resource:///modules/devtools/dbg-server.jsm
- */
-
-function MarionetteRootActor(aConnection)
-{
-  this.conn = aConnection;
-  this._marionetteActor = new MarionetteDriverActor(this.conn);
-  this._marionetteActorPool = null; //hold future actors
-
-  this._marionetteActorPool = new ActorPool(this.conn);
-  this._marionetteActorPool.addActor(this._marionetteActor);
-  this.conn.addActorPool(this._marionetteActorPool);
-}
-
-MarionetteRootActor.prototype = {
-  /**
-   * Called when a client first makes a connection
-   *
-   * @return object
-   *         returns the name of the actor, the application type, and any traits
-   */
-  sayHello: function MRA_sayHello() {
-    return { from: "root",
-             applicationType: "gecko",
-             traits: [] };
-  },
-
-  /**
-   * Called when client disconnects. Cleans up marionette driver actions.
-   */
-  disconnect: function MRA_disconnect() {
-    this._marionetteActor.deleteSession();
-  },
-
-  /**
-   * Used to get the running marionette actor, so we can issue commands
-   *
-   * @return object
-   *         Returns the ID the client can use to communicate with the
-   *         MarionetteDriverActor
-   */
-  getMarionetteID: function MRA_getMarionette() {
-    return { "from": "root",
-             "id": this._marionetteActor.actorID } ;
-  }
-};
-
-// register the calls
-MarionetteRootActor.prototype.requestTypes = {
-  "getMarionetteID": MarionetteRootActor.prototype.getMarionetteID,
-  "sayHello": MarionetteRootActor.prototype.sayHello
-};
-
-/**
  * An object representing a frame that Marionette has loaded a
  * frame script in.
  */
 function MarionetteRemoteFrame(windowId, frameId) {
   this.windowId = windowId;
   this.frameId = frameId;
   this.targetFrameId = null;
   this.messageManager = null;
@@ -168,26 +104,35 @@ function FrameSendFailureError(frame) {
   this.frame = frame;
   this.message = "Error sending message to frame (NS_ERROR_FAILURE)";
   this.toString = function() {
     return this.message + " " + this.frame + "; frame not responding.";
   }
 }
 
 /**
- * This actor is responsible for all marionette API calls. It gets created
+ * The server connection is responsible for all marionette API calls. It gets created
  * for each connection and manages all chrome and browser based calls. It
  * mediates content calls by issuing appropriate messages to the content process.
  */
-function MarionetteDriverActor(aConnection)
+function MarionetteServerConnection(aPrefix, aTransport, aServer)
 {
   this.uuidGen = Cc["@mozilla.org/uuid-generator;1"]
                    .getService(Ci.nsIUUIDGenerator);
 
-  this.conn = aConnection;
+  this.prefix = aPrefix;
+  this.server = aServer;
+  this.conn = aTransport;
+  this.conn.hooks = this;
+
+  // marionette uses a protocol based on the debugger server, which requires
+  // passing back "actor ids" with responses. unlike the debugger server,
+  // we don't have multiple actors, so just use a dummy value of "0" here
+  this.actorID = "0";
+
   this.globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"]
                              .getService(Ci.nsIMessageBroadcaster);
   this.messageManager = this.globalMessageManager;
   this.browsers = {}; //holds list of BrowserObjs
   this.curBrowser = null; // points to current browser
   this.context = "content";
   this.scriptTimeout = null;
   this.searchTimeout = null;
@@ -201,20 +146,43 @@ function MarionetteDriverActor(aConnecti
   this.currentRemoteFrame = null; // a member of remoteFrames
   this.testName = null;
   this.mozBrowserClose = null;
 
   //register all message listeners
   this.addMessageManagerListeners(this.messageManager);
 }
 
-MarionetteDriverActor.prototype = {
+MarionetteServerConnection.prototype = {
 
-  //name of the actor
-  actorPrefix: "marionette",
+  /**
+   * Debugger transport callbacks:
+   */
+  onPacket: function MSC_onPacket(aPacket) {
+    // Dispatch the request
+    if (this.requestTypes && this.requestTypes[aPacket.type]) {
+      try {
+        this.requestTypes[aPacket.type].bind(this)(aPacket);
+      } catch(e) {
+        this.conn.send({ error: ("error occurred while processing '" +
+                                 aPacket.type),
+                        message: e });
+      }
+    } else {
+      this.conn.send({ error: "unrecognizedPacketType",
+                       message: ('Marionette does not ' +
+                                 'recognize the packet type "' +
+                                 aPacket.type + '"') });
+    }
+  },
+
+  onClosed: function MSC_onClosed(aStatus) {
+    this.server._connectionClosed(this);
+    this.deleteSession();
+  },
 
   /**
    * Helper methods:
    */
 
   /**
    * Switches to the global ChromeMessageBroadcaster, potentially replacing a frame-specific
    * ChromeMessageSender.  Has no effect if the global ChromeMessageBroadcaster is already
@@ -365,16 +333,26 @@ MarionetteDriverActor.prototype = {
    *        Used to distinguish the asynchronous responses.
    */
   sendResponse: function MDA_sendResponse(value, command_id) {
     if (typeof(value) == 'undefined')
         value = null;
     this.sendToClient({from:this.actorID, value: value}, command_id);
   },
 
+  sayHello: function MDA_sayHello() {
+    this.conn.send({ from: "root",
+                     applicationType: "gecko",
+                     traits: [] });
+  },
+
+  getMarionetteID: function MDA_getMarionette() {
+    this.conn.send({ "from": "root", "id": this.actorID });
+  },
+
   /**
    * Send ack to client
    * 
    * @param string command_id
    *        Unique identifier assigned to the client's request.
    *        Used to distinguish the asynchronous responses.
    */
   sendOk: function MDA_sendOk(command_id) {
@@ -2182,70 +2160,72 @@ MarionetteDriverActor.prototype = {
             this.newSessionCommandId = null;
           }
         }
         return reg;
     }
   }
 };
 
-MarionetteDriverActor.prototype.requestTypes = {
-  "newSession": MarionetteDriverActor.prototype.newSession,
-  "getSessionCapabilities": MarionetteDriverActor.prototype.getSessionCapabilities,
-  "getStatus": MarionetteDriverActor.prototype.getStatus,
-  "log": MarionetteDriverActor.prototype.log,
-  "getLogs": MarionetteDriverActor.prototype.getLogs,
-  "setContext": MarionetteDriverActor.prototype.setContext,
-  "executeScript": MarionetteDriverActor.prototype.execute,
-  "setScriptTimeout": MarionetteDriverActor.prototype.setScriptTimeout,
-  "timeouts": MarionetteDriverActor.prototype.timeouts,
-  "singleTap": MarionetteDriverActor.prototype.singleTap,
-  "actionChain": MarionetteDriverActor.prototype.actionChain,
-  "multiAction": MarionetteDriverActor.prototype.multiAction,
-  "executeAsyncScript": MarionetteDriverActor.prototype.executeWithCallback,
-  "executeJSScript": MarionetteDriverActor.prototype.executeJSScript,
-  "setSearchTimeout": MarionetteDriverActor.prototype.setSearchTimeout,
-  "findElement": MarionetteDriverActor.prototype.findElement,
-  "findElements": MarionetteDriverActor.prototype.findElements,
-  "clickElement": MarionetteDriverActor.prototype.clickElement,
-  "getElementAttribute": MarionetteDriverActor.prototype.getElementAttribute,
-  "getElementText": MarionetteDriverActor.prototype.getElementText,
-  "getElementTagName": MarionetteDriverActor.prototype.getElementTagName,
-  "isElementDisplayed": MarionetteDriverActor.prototype.isElementDisplayed,
-  "getElementValueOfCssProperty": MarionetteDriverActor.prototype.getElementValueOfCssProperty,
-  "getElementSize": MarionetteDriverActor.prototype.getElementSize,
-  "isElementEnabled": MarionetteDriverActor.prototype.isElementEnabled,
-  "isElementSelected": MarionetteDriverActor.prototype.isElementSelected,
-  "sendKeysToElement": MarionetteDriverActor.prototype.sendKeysToElement,
-  "getElementPosition": MarionetteDriverActor.prototype.getElementPosition,
-  "clearElement": MarionetteDriverActor.prototype.clearElement,
-  "getTitle": MarionetteDriverActor.prototype.getTitle,
-  "getWindowType": MarionetteDriverActor.prototype.getWindowType,
-  "getPageSource": MarionetteDriverActor.prototype.getPageSource,
-  "goUrl": MarionetteDriverActor.prototype.goUrl,
-  "getUrl": MarionetteDriverActor.prototype.getUrl,
-  "goBack": MarionetteDriverActor.prototype.goBack,
-  "goForward": MarionetteDriverActor.prototype.goForward,
-  "refresh":  MarionetteDriverActor.prototype.refresh,
-  "getWindow":  MarionetteDriverActor.prototype.getWindow,
-  "getWindows":  MarionetteDriverActor.prototype.getWindows,
-  "switchToFrame": MarionetteDriverActor.prototype.switchToFrame,
-  "switchToWindow": MarionetteDriverActor.prototype.switchToWindow,
-  "deleteSession": MarionetteDriverActor.prototype.deleteSession,
-  "emulatorCmdResult": MarionetteDriverActor.prototype.emulatorCmdResult,
-  "importScript": MarionetteDriverActor.prototype.importScript,
-  "getAppCacheStatus": MarionetteDriverActor.prototype.getAppCacheStatus,
-  "closeWindow": MarionetteDriverActor.prototype.closeWindow,
-  "setTestName": MarionetteDriverActor.prototype.setTestName,
-  "screenShot": MarionetteDriverActor.prototype.screenShot,
-  "addCookie": MarionetteDriverActor.prototype.addCookie,
-  "getAllCookies": MarionetteDriverActor.prototype.getAllCookies,
-  "deleteAllCookies": MarionetteDriverActor.prototype.deleteAllCookies,
-  "deleteCookie": MarionetteDriverActor.prototype.deleteCookie,
-  "getActiveElement": MarionetteDriverActor.prototype.getActiveElement
+MarionetteServerConnection.prototype.requestTypes = {
+  "getMarionetteID": MarionetteServerConnection.prototype.getMarionetteID,
+  "sayHello": MarionetteServerConnection.prototype.sayHello,
+  "newSession": MarionetteServerConnection.prototype.newSession,
+  "getSessionCapabilities": MarionetteServerConnection.prototype.getSessionCapabilities,
+  "getStatus": MarionetteServerConnection.prototype.getStatus,
+  "log": MarionetteServerConnection.prototype.log,
+  "getLogs": MarionetteServerConnection.prototype.getLogs,
+  "setContext": MarionetteServerConnection.prototype.setContext,
+  "executeScript": MarionetteServerConnection.prototype.execute,
+  "setScriptTimeout": MarionetteServerConnection.prototype.setScriptTimeout,
+  "timeouts": MarionetteServerConnection.prototype.timeouts,
+  "singleTap": MarionetteServerConnection.prototype.singleTap,
+  "actionChain": MarionetteServerConnection.prototype.actionChain,
+  "multiAction": MarionetteServerConnection.prototype.multiAction,
+  "executeAsyncScript": MarionetteServerConnection.prototype.executeWithCallback,
+  "executeJSScript": MarionetteServerConnection.prototype.executeJSScript,
+  "setSearchTimeout": MarionetteServerConnection.prototype.setSearchTimeout,
+  "findElement": MarionetteServerConnection.prototype.findElement,
+  "findElements": MarionetteServerConnection.prototype.findElements,
+  "clickElement": MarionetteServerConnection.prototype.clickElement,
+  "getElementAttribute": MarionetteServerConnection.prototype.getElementAttribute,
+  "getElementText": MarionetteServerConnection.prototype.getElementText,
+  "getElementTagName": MarionetteServerConnection.prototype.getElementTagName,
+  "isElementDisplayed": MarionetteServerConnection.prototype.isElementDisplayed,
+  "getElementValueOfCssProperty": MarionetteServerConnection.prototype.getElementValueOfCssProperty,
+  "getElementSize": MarionetteServerConnection.prototype.getElementSize,
+  "isElementEnabled": MarionetteServerConnection.prototype.isElementEnabled,
+  "isElementSelected": MarionetteServerConnection.prototype.isElementSelected,
+  "sendKeysToElement": MarionetteServerConnection.prototype.sendKeysToElement,
+  "getElementPosition": MarionetteServerConnection.prototype.getElementPosition,
+  "clearElement": MarionetteServerConnection.prototype.clearElement,
+  "getTitle": MarionetteServerConnection.prototype.getTitle,
+  "getWindowType": MarionetteServerConnection.prototype.getWindowType,
+  "getPageSource": MarionetteServerConnection.prototype.getPageSource,
+  "goUrl": MarionetteServerConnection.prototype.goUrl,
+  "getUrl": MarionetteServerConnection.prototype.getUrl,
+  "goBack": MarionetteServerConnection.prototype.goBack,
+  "goForward": MarionetteServerConnection.prototype.goForward,
+  "refresh":  MarionetteServerConnection.prototype.refresh,
+  "getWindow":  MarionetteServerConnection.prototype.getWindow,
+  "getWindows":  MarionetteServerConnection.prototype.getWindows,
+  "switchToFrame": MarionetteServerConnection.prototype.switchToFrame,
+  "switchToWindow": MarionetteServerConnection.prototype.switchToWindow,
+  "deleteSession": MarionetteServerConnection.prototype.deleteSession,
+  "emulatorCmdResult": MarionetteServerConnection.prototype.emulatorCmdResult,
+  "importScript": MarionetteServerConnection.prototype.importScript,
+  "getAppCacheStatus": MarionetteServerConnection.prototype.getAppCacheStatus,
+  "closeWindow": MarionetteServerConnection.prototype.closeWindow,
+  "setTestName": MarionetteServerConnection.prototype.setTestName,
+  "screenShot": MarionetteServerConnection.prototype.screenShot,
+  "addCookie": MarionetteServerConnection.prototype.addCookie,
+  "getAllCookies": MarionetteServerConnection.prototype.getAllCookies,
+  "deleteAllCookies": MarionetteServerConnection.prototype.deleteAllCookies,
+  "deleteCookie": MarionetteServerConnection.prototype.deleteCookie,
+  "getActiveElement": MarionetteServerConnection.prototype.getActiveElement
 };
 
 /**
  * Creates a BrowserObj. BrowserObjs handle interactions with the
  * browser, according to the current environment (desktop, b2g, etc.)
  *
  * @param nsIDOMWindow win
  *        The window whose browser needs to be accessed
@@ -2373,8 +2353,52 @@ BrowserObj.prototype = {
         this.curFrameId = uid;
         this.mainContentId = uid;
       }
     }
     this.knownFrames.push(uid); //used to delete sessions
     return uid;
   },
 }
+
+/**
+ * Marionette server -- this class holds a reference to a socket and creates
+ * MarionetteServerConnection objects as needed.
+ */
+this.MarionetteServer = function MarionetteServer(port, forceLocal) {
+  let flags = Ci.nsIServerSocket.KeepWhenOffline;
+  if (forceLocal) {
+    flags |= Ci.nsIServerSocket.LoopbackOnly;
+  }
+  let socket = new ServerSocket(port, flags, 4);
+  logger.info("Listening on port " + socket.port + "\n");
+  socket.asyncListen(this);
+  this.listener = socket;
+  this.nextConnId = 0;
+  this.connections = {};
+};
+
+MarionetteServer.prototype = {
+  onSocketAccepted: function(serverSocket, clientSocket)
+  {
+    logger.debug("accepted connection on " + clientSocket.host + ":" + clientSocket.port);
+
+    let input = clientSocket.openInputStream(0, 0, 0);
+    let output = clientSocket.openOutputStream(0, 0, 0);
+    let aTransport = new DebuggerTransport(input, output);
+    let connID = "conn" + this.nextConnID++ + '.';
+    let conn = new MarionetteServerConnection(connID, aTransport, this);
+    this.connections[connID] = conn;
+
+    // Create a root actor for the connection and send the hello packet.
+    conn.sayHello();
+    aTransport.ready();
+  },
+
+  closeListener: function() {
+    this.listener.close();
+    this.listener = null;
+  },
+
+  _connectionClosed: function DS_connectionClosed(aConnection) {
+    delete this.connections[aConnection.prefix];
+  }
+};
--- a/testing/marionette/marionette-simpletest.js
+++ b/testing/marionette/marionette-simpletest.js
@@ -1,16 +1,16 @@
 /* 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/. */
 /*
  * The Marionette object, passed to the script context.
  */
 
-function Marionette(scope, window, context, logObj, timeout, testName) {
+this.Marionette = function Marionette(scope, window, context, logObj, timeout, testName) {
   this.scope = scope;
   this.window = window;
   this.tests = [];
   this.logObj = logObj;
   this.context = context;
   this.timeout = timeout;
   this.testName = testName;
   this.TEST_UNEXPECTED_FAIL = "TEST-UNEXPECTED-FAIL";