Backed out 16 changesets (bug 1107706) for Gaia JS integration test failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 24 Mar 2015 13:54:55 -0400
changeset 264235 4c2c50594967c482da30466957afb0e3959f191e
parent 264234 e0260f535d85ad092098111865938e98a73d043b
child 264236 7be28b1e114c9c2443cd0a1611a49d90374d28f2
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1107706
milestone39.0a1
backs outd026794b4c0b4a95414deaab831da79c27232586
bb481b2d170ac58f77b9c6e43b3b47063fec4160
71eba829a8b4d009ad6c34018b6d9467f6acb866
3ca5a996676e64c796d6190b5d7b99edd2378266
18c48c6a0cd5a040832959351ccf8783c252e19e
5dce917aeb925d0e8e0ae412d73414fb97a06140
933d7aa1c709d5b67ae24003f8a4ab45a324a4b1
0c6e1484ae7a6f082c2037f9c6e5e705874dd33b
9972f443d70eb15554965978301d204003a5f431
20f9b7b24fc50c83afd07eced7a1678a53185c45
1f4ba5b0fc4f6c765705212c17eda4bbb273b4e9
a4a8e755d81552e00cb0b79d6af95c1d42aee38e
593a7917f9170a833d2418d8742e9a74c5afe758
502320aec21f189c534f1cf3739dee38088996c1
60b58aed6d27d4855c60ca81a68406c629ea4f51
c8315bbbc1047c011db78b8fa1b3fd4c4787eb5f
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
Backed out 16 changesets (bug 1107706) for Gaia JS integration test failures. Backed out changeset d026794b4c0b (bug 1107706) Backed out changeset bb481b2d170a (bug 1107706) Backed out changeset 71eba829a8b4 (bug 1107706) Backed out changeset 3ca5a996676e (bug 1107706) Backed out changeset 18c48c6a0cd5 (bug 1107706) Backed out changeset 5dce917aeb92 (bug 1107706) Backed out changeset 933d7aa1c709 (bug 1107706) Backed out changeset 0c6e1484ae7a (bug 1107706) Backed out changeset 9972f443d70e (bug 1107706) Backed out changeset 20f9b7b24fc5 (bug 1107706) Backed out changeset 1f4ba5b0fc4f (bug 1107706) Backed out changeset a4a8e755d815 (bug 1107706) Backed out changeset 593a7917f917 (bug 1107706) Backed out changeset 502320aec21f (bug 1107706) Backed out changeset 60b58aed6d27 (bug 1107706) Backed out changeset c8315bbbc104 (bug 1107706) CLOSED TREE
testing/marionette/actions.js
testing/marionette/client/marionette/tests/unit/test_elementsize.py
testing/marionette/client/marionette/tests/unit/test_execute_script.py
testing/marionette/client/marionette/tests/unit/unit-tests.ini
testing/marionette/command.js
testing/marionette/common.js
testing/marionette/components/marionettecomponent.js
testing/marionette/dispatcher.js
testing/marionette/driver.js
testing/marionette/elements.js
testing/marionette/emulator.js
testing/marionette/error.js
testing/marionette/frame-manager.js
testing/marionette/jar.mn
testing/marionette/listener.js
testing/marionette/marionette-actions.js
testing/marionette/marionette-common.js
testing/marionette/marionette-elements.js
testing/marionette/marionette-frame-manager.js
testing/marionette/marionette-listener.js
testing/marionette/marionette-sendkeys.js
testing/marionette/marionette-server.js
testing/marionette/marionette-simpletest.js
testing/marionette/modal.js
testing/marionette/sendkeys.js
testing/marionette/server.js
testing/marionette/simpletest.js
testing/specialpowers/content/SpecialPowersObserverAPI.js
--- a/testing/marionette/client/marionette/tests/unit/test_elementsize.py
+++ b/testing/marionette/client/marionette/tests/unit/test_elementsize.py
@@ -1,33 +1,29 @@
 # 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/.
 
-from marionette.marionette_test import MarionetteTestCase, skip_if_b2g
-
+from marionette import MarionetteTestCase
 
 class TestElementSize(MarionetteTestCase):
     def testShouldReturnTheSizeOfALink(self):
         test_html = self.marionette.absolute_url("testSize.html")
         self.marionette.navigate(test_html)
         shrinko = self.marionette.find_element('id', 'linkId')
         size = shrinko.rect
         self.assertTrue(size['width'] > 0)
         self.assertTrue(size['height'] > 0)
 
-
-@skip_if_b2g
 class TestElementSizeChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
-        self.marionette.execute_script(
-            "window.open('chrome://marionette/content/test2.xul', 'foo', 'chrome,centerscreen');")
+        self.marionette.execute_script("window.open('chrome://marionette/content/test2.xul', 'foo', 'chrome,centerscreen');")
         self.marionette.switch_to_window('foo')
         self.assertNotEqual(self.win, self.marionette.current_window_handle)
 
     def tearDown(self):
         self.assertNotEqual(self.win, self.marionette.current_window_handle)
         self.marionette.execute_script("window.close();")
         self.marionette.switch_to_window(self.win)
         MarionetteTestCase.tearDown(self)
@@ -36,8 +32,9 @@ class TestElementSizeChrome(MarionetteTe
         wins = self.marionette.window_handles
         wins.remove(self.win)
         newWin = wins.pop()
         self.marionette.switch_to_window(newWin)
         shrinko = self.marionette.find_element('id', 'textInput')
         size = shrinko.rect
         self.assertTrue(size['width'] > 0)
         self.assertTrue(size['height'] > 0)
+
--- a/testing/marionette/client/marionette/tests/unit/test_execute_script.py
+++ b/testing/marionette/client/marionette/tests/unit/test_execute_script.py
@@ -1,51 +1,49 @@
 # 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/.
 
 import urllib
 
-from marionette_driver import By, errors
-from marionette.marionette_test import MarionetteTestCase, skip_if_b2g
-
+from marionette_driver.by import By
+from marionette_driver.errors import JavascriptException
+from marionette import MarionetteTestCase
 
 def inline(doc):
     return "data:text/html;charset=utf-8,%s" % urllib.quote(doc)
 
-
 elements = inline("<p>foo</p> <p>bar</p>")
 
-
 class TestExecuteContent(MarionetteTestCase):
     def test_stack_trace(self):
         try:
             self.marionette.execute_script("""
                 let a = 1;
                 return b;
                 """)
             self.assertFalse(True)
-        except errors.JavascriptException as inst:
+        except JavascriptException, inst:
             self.assertTrue('return b' in inst.stacktrace)
 
     def test_execute_simple(self):
         self.assertEqual(1, self.marionette.execute_script("return 1;"))
 
     def test_check_window(self):
         self.assertTrue(self.marionette.execute_script("return (window !=null && window != undefined);"))
 
     def test_execute_no_return(self):
         self.assertEqual(self.marionette.execute_script("1;"), None)
 
     def test_execute_js_exception(self):
-        self.assertRaises(errors.JavascriptException,
+        self.assertRaises(JavascriptException,
             self.marionette.execute_script, "return foo(bar);")
 
     def test_execute_permission(self):
-        self.assertRaises(errors.JavascriptException,
+        self.assertRaises(JavascriptException,
                           self.marionette.execute_script,
                           """
 let prefs = Components.classes["@mozilla.org/preferences-service;1"]
                               .getService(Components.interfaces.nsIPrefBranch);
 """)
 
     def test_complex_return_values(self):
         self.assertEqual(self.marionette.execute_script("return [1, 2];"), [1, 2])
@@ -93,18 +91,16 @@ let prefs = Components.classes["@mozilla
         self.assertTrue(isinstance(result, float))
         self.assertEqual(result, expected_result)
 
     def test_null_argument(self):
         result = self.marionette.execute_script("return arguments[0]",
                                                 [None])
         self.assertIs(result, None)
 
-
-@skip_if_b2g
 class TestExecuteChrome(TestExecuteContent):
     def setUp(self):
         super(TestExecuteChrome, self).setUp()
         self.win = self.marionette.current_window_handle
         self.marionette.set_context("chrome")
         self.marionette.execute_script(
             "window.open('chrome://marionette/content/test.xul', 'xul', 'chrome')")
         self.marionette.switch_to_window("xul")
@@ -120,15 +116,8 @@ class TestExecuteChrome(TestExecuteConte
         self.assertEqual(1, self.marionette.execute_script(
             "var c = Components.classes; return 1;"))
 
     def test_unmarshal_element_collection(self):
         expected = self.marionette.find_elements(By.TAG_NAME, "textbox")
         actual = self.marionette.execute_script(
             "return document.querySelectorAll('textbox')")
         self.assertEqual(expected, actual)
-
-    def test_async_script_timeout(self):
-        with self.assertRaises(errors.ScriptTimeoutException):
-            self.marionette.execute_async_script("""
-                var cb = arguments[arguments.length - 1];
-                setTimeout(function() { cb() }, 250);
-                """, script_timeout=100)
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -91,17 +91,16 @@ b2g = true
 browser = false
 
 [test_simpletest_pass.js]
 [test_simpletest_sanity.py]
 [test_simpletest_chrome.js]
 [test_simpletest_timeout.js]
 [test_specialpowers.py]
 [test_anonymous_content.py]
-b2g = false
 [test_switch_frame.py]
 b2g = false
 skip-if = os == "win" # Bug 1078237
 [test_switch_frame_chrome.py]
 b2g = false
 [test_switch_remote_frame.py]
 b2g = false
 
deleted file mode 100644
--- a/testing/marionette/command.js
+++ /dev/null
@@ -1,168 +0,0 @@
-/* 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";
-
-const {utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-
-Cu.import("chrome://marionette/content/error.js");
-
-this.EXPORTED_SYMBOLS = ["CommandProcessor", "Response"];
-const logger = Log.repository.getLogger("Marionette");
-
-/**
- * Represents the response returned from the remote end after execution
- * of its corresponding command.
- *
- * The Response is a mutable object passed to each command for
- * modification through the available setters.  The response is sent
- * implicitly by CommandProcessor when a command is finished executing,
- * and any modifications made subsequent to this will have no effect.
- *
- * @param {number} cmdId
- *     UUID tied to the corresponding command request this is
- *     a response for.
- * @param {function(number)} okHandler
- *     Callback function called on successful responses with no body.
- * @param {function(Object, number)} respHandler
- *     Callback function called on successful responses with body.
- * @param {Object=} msg
- *     A message to populate the response, containing the properties
- *     "sessionId", "status", and "value".
- * @param {function(Map)=} sanitizer
- *     Run before sending message.
- */
-this.Response = function(cmdId, okHandler, respHandler, msg, sanitizer) {
-  const removeEmpty = function(map) {
-    let rv = {};
-    for (let [key, value] of map) {
-      if (typeof value == "undefined") {
-        value = null;
-      }
-      rv[key] = value;
-    }
-    return rv;
-  };
-
-  this.id = cmdId;
-  this.ok = true;
-  this.okHandler = okHandler;
-  this.respHandler = respHandler;
-  this.sanitizer = sanitizer || removeEmpty;
-
-  this.data = new Map([
-    ["sessionId", msg.sessionId ? msg.sessionId : null],
-    ["status", msg.status ? msg.status : 0 /* success */],
-    ["value", msg.value ? msg.value : undefined],
-  ]);
-};
-
-Response.prototype = {
-  get name() { return this.data.get("name"); },
-  set name(n) { this.data.set("name", n); },
-  get sessionId() { return this.data.get("sessionId"); },
-  set sessionId(id) { this.data.set("sessionId", id); },
-  get status() { return this.data.get("status"); },
-  set status(ns) { this.data.set("status", ns); },
-  get value() { return this.data.get("value"); },
-  set value(val) {
-    this.data.set("value", val);
-    this.ok = false;
-  }
-};
-
-Response.prototype.send = function() {
-  if (this.sent) {
-    logger.warn("Skipped sending response to command ID " +
-      this.id + " because response has already been sent");
-    return;
-  }
-
-  if (this.ok) {
-    this.okHandler(this.id);
-  } else {
-    let rawData = this.sanitizer(this.data);
-    this.respHandler(rawData, this.id);
-  }
-};
-
-/**
- * @param {(Error|Object)} err
- *     The error to send, either an instance of the Error prototype,
- *     or an object with the properties "message", "code", and "stack".
- */
-Response.prototype.sendError = function(err) {
-  this.status = "code" in err ? err.code : new UnknownError().code;
-  this.value = error.toJSON(err);
-  this.send();
-
-  // propagate errors that are implementation problems
-  if (!error.isWebDriverError(err)) {
-    throw err;
-  }
-};
-
-/**
- * The command processor receives messages on execute(payload, …)
- * from the dispatcher, processes them, and wraps the functions that
- * it executes from the WebDriver implementation, driver.
- *
- * @param {GeckoDriver} driver
- *     Reference to the driver implementation.
- */
-this.CommandProcessor = function(driver) {
-  this.driver = driver;
-};
-
-/**
- * Executes a WebDriver command based on the received payload,
- * which is expected to be an object with a "parameters" property
- * that is a simple key/value collection of arguments.
- *
- * The respHandler function will be called with the JSON object to
- * send back to the client.
- *
- * The cmdId is the UUID tied to this request that prevents
- * the dispatcher from sending responses in the wrong order.
- *
- * @param {Object} payload
- *     Message as received from client.
- * @param {function(number)} okHandler
- *     Callback function called on successful responses with no body.
- * @param {function(Object, number)} respHandler
- *     Callback function called on successful responses with body.
- * @param {number} cmdId
- *     The unique identifier for the command to execute.
- */
-CommandProcessor.prototype.execute = function(payload, okHandler, respHandler, cmdId) {
-  let cmd = payload;
-  let resp = new Response(
-    cmdId, okHandler, respHandler, {sessionId: this.driver.sessionId});
-  let sendResponse = resp.send.bind(resp);
-  let sendError = resp.sendError.bind(resp);
-
-  // Ideally handlers shouldn't have to care about the command ID,
-  // but some methods (newSession, executeScript, et al.) have not
-  // yet been converted to use the new form of request dispatching.
-  cmd.id = cmdId;
-
-  // For as long as the old form of request dispatching is in use,
-  // we need to tell ListenerProxy what the current command ID is
-  // so that individual commands in driver.js can define it explicitly.
-  this.driver.listener.curCmdId = cmd.id;
-
-  let req = Task.spawn(function*() {
-    let fn = this.driver.commands[cmd.name];
-    if (typeof fn == "undefined") {
-      throw new UnknownCommandError(cmd.name);
-    }
-
-    yield fn.bind(this.driver)(cmd, resp);
-  }.bind(this));
-
-  req.then(sendResponse, sendError).catch(error.report);
-};
--- a/testing/marionette/components/marionettecomponent.js
+++ b/testing/marionette/components/marionettecomponent.js
@@ -1,190 +1,187 @@
 /* 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";
-
-const {Constructor: CC, interfaces: Ci, utils: Cu} = Components;
+this.CC = Components.Constructor;
+this.Cc = Components.classes;
+this.Ci = Components.interfaces;
+this.Cu = Components.utils;
+this.Cr = Components.results;
 
 const MARIONETTE_CONTRACTID = "@mozilla.org/marionette;1";
 const MARIONETTE_CID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}");
+const MARIONETTE_ENABLED_PREF = 'marionette.defaultPrefs.enabled';
+const MARIONETTE_FORCELOCAL_PREF = 'marionette.force-local';
+const MARIONETTE_LOG_PREF = 'marionette.logging';
 
-const DEFAULT_PORT = 2828;
-const ENABLED_PREF = "marionette.defaultPrefs.enabled";
-const PORT_PREF = "marionette.defaultPrefs.port";
-const FORCELOCAL_PREF = "marionette.force-local";
-const LOG_PREF = "marionette.logging";
-
-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/Log.jsm");
 
+let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
+               .getService(Ci.mozIJSSubScriptLoader);
+
 function MarionetteComponent() {
-  this.loaded_ = false;
+  this._loaded = false;
   this.observerService = Services.obs;
 
+  // set up the logger
   this.logger = Log.repository.getLogger("Marionette");
-  this.logger.level = Log.Level.Trace;
+  this.logger.level = Log.Level["Trace"];
   let dumper = false;
 #ifdef DEBUG
   dumper = true;
 #endif
 #ifdef MOZ_B2G
   dumper = true;
 #endif
   try {
-    if (dumper || Services.prefs.getBoolPref(LOG_PREF)) {
+    if (dumper || Services.prefs.getBoolPref(MARIONETTE_LOG_PREF)) {
       let formatter = new Log.BasicFormatter();
       this.logger.addAppender(new Log.DumpAppender(formatter));
     }
-  } catch(e) {}
+  }
+  catch(e) {}
 }
 
 MarionetteComponent.prototype = {
   classDescription: "Marionette component",
   classID: MARIONETTE_CID,
   contractID: MARIONETTE_CONTRACTID,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler, Ci.nsIObserver]),
-  _xpcom_categories: [
-    {category: "command-line-handler", entry: "b-marionette"},
-    {category: "profile-after-change", service: true}
-  ],
+  _xpcom_categories: [{category: "command-line-handler", entry: "b-marionette"},
+                      {category: "profile-after-change", service: true}],
+  appName: Services.appinfo.name,
   enabled: false,
   finalUiStartup: false,
-  server: null
-};
+  _marionetteServer: null,
 
-MarionetteComponent.prototype.onSocketAccepted = function(
-    socket, transport) {
-  this.logger.info("onSocketAccepted for Marionette dummy socket");
-};
+  onSocketAccepted: function mc_onSocketAccepted(aSocket, aTransport) {
+    this.logger.info("onSocketAccepted for Marionette dummy socket");
+  },
 
-MarionetteComponent.prototype.onStopListening = function(socket, status) {
-  this.logger.info(`onStopListening for Marionette dummy socket, code ${status}`);
-  socket.close();
-};
+  onStopListening: function mc_onStopListening(aSocket, status) {
+    this.logger.info("onStopListening for Marionette dummy socket, code " + status);
+    aSocket.close();
+  },
 
-/** Check cmdLine argument for {@code --marionette}. */
-MarionetteComponent.prototype.handle = function(cmdLine) {
-  // if the CLI is there then lets do work otherwise nothing to see
-  if (cmdLine.handleFlag("marionette", false)) {
-    this.enabled = true;
-    this.logger.info("Marionette enabled via command-line flag");
-    this.init();
-  }
-};
+  // Check cmdLine argument for --marionette
+  handle: function mc_handle(cmdLine) {
+    // If the CLI is there then lets do work otherwise nothing to see
+    if (cmdLine.handleFlag("marionette", false)) {
+      this.enabled = true;
+      this.logger.info("marionette enabled via command-line");
+      this.init();
+    }
+  },
 
-MarionetteComponent.prototype.observe = function(subj, topic, data) {
-  switch (topic) {
-    case "profile-after-change":
-      // Using final-ui-startup as the xpcom category doesn't seem to work,
-      // so we wait for that by adding an observer here.
-      this.observerService.addObserver(this, "final-ui-startup", false);
+  observe: function mc_observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "profile-after-change":
+        // Using final-ui-startup as the xpcom category doesn't seem to work,
+        // so we wait for that by adding an observer here.
+        this.observerService.addObserver(this, "final-ui-startup", false);
 #ifdef ENABLE_MARIONETTE
-      try {
-        this.enabled = Services.prefs.getBoolPref(ENABLED_PREF);
-      } catch(e) {}
-      if (this.enabled) {
-        this.logger.info("Marionette enabled via build flag and pref");
-
-        // We want to suppress the modal dialog that's shown
-        // when starting up in safe-mode to enable testing.
-        if (Services.appinfo.inSafeMode) {
-          this.observerService.addObserver(this, "domwindowopened", false);
-        }
-      }
-#endif
-      break;
+        let enabledPref = false;
+        try {
+          enabledPref = Services.prefs.getBoolPref(MARIONETTE_ENABLED_PREF);
+        } catch(e) {}
+        if (enabledPref) {
+          this.enabled = true;
+          this.logger.info("marionette enabled via build flag and pref");
 
-    case "final-ui-startup":
-      this.finalUiStartup = true;
-      this.observerService.removeObserver(this, topic);
-      this.observerService.addObserver(this, "xpcom-shutdown", false);
-      this.init();
-      break;
-
-    case "domwindowopened":
-      this.observerService.removeObserver(this, topic);
-      this.suppressSafeModeDialog_(subj);
-      break;
-
-    case "xpcom-shutdown":
-      this.observerService.removeObserver(this, "xpcom-shutdown");
-      this.uninit();
-      break;
-  }
-};
+          // We want to suppress the modal dialog that's shown
+          // when starting up in safe-mode to enable testing.
+          if (Services.appinfo.inSafeMode) {
+            this.observerService.addObserver(this, "domwindowopened", false);
+          }
+        }
+#endif
+        break;
+      case "final-ui-startup":
+        this.finalUiStartup = true;
+        this.observerService.removeObserver(this, aTopic);
+        this.observerService.addObserver(this, "xpcom-shutdown", false);
+        this.init();
+        break;
+      case "domwindowopened":
+        this.observerService.removeObserver(this, aTopic);
+        this._suppressSafeModeDialog(aSubject);
+        break;
+      case "xpcom-shutdown":
+        this.observerService.removeObserver(this, "xpcom-shutdown");
+        this.uninit();
+        break;
+    }
+  },
 
-MarionetteComponent.prototype.suppressSafeModeDialog_ = function(win) {
-  // Wait for the modal dialog to finish loading.
-  win.addEventListener("load", function onload() {
-    win.removeEventListener("load", onload);
+  _suppressSafeModeDialog: function mc_suppressSafeModeDialog(aWindow) {
+    // Wait for the modal dialog to finish loading.
+    aWindow.addEventListener("load", function onLoad() {
+      aWindow.removeEventListener("load", onLoad);
 
-    if (win.document.getElementById("safeModeDialog")) {
-      // Accept the dialog to start in safe-mode
-      win.setTimeout(() => {
-        win.document.documentElement.getButton("accept").click();
-      });
-    }
-  });
-};
+      if (aWindow.document.getElementById("safeModeDialog")) {
+        aWindow.setTimeout(() => {
+          // Accept the dialog to start in safe-mode.
+          aWindow.document.documentElement.getButton("accept").click();
+        });
+      }
+    });
+  },
 
-MarionetteComponent.prototype.init = function() {
-  if (this.loaded_ || !this.enabled || !this.finalUiStartup) {
-    return;
-  }
+  init: function mc_init() {
+    if (!this._loaded && this.enabled && this.finalUiStartup) {
+      this._loaded = true;
 
-  this.loaded_ = true;
-
-  let forceLocal = Services.appinfo.name == "B2G" ? false : true;
-  try {
-    forceLocal = Services.prefs.getBoolPref(FORCELOCAL_PREF);
-  } catch (e) {}
-  Services.prefs.setBoolPref(FORCELOCAL_PREF, forceLocal);
+      let marionette_forcelocal = this.appName == 'B2G' ? false : true;
+      try {
+        marionette_forcelocal = Services.prefs.getBoolPref(MARIONETTE_FORCELOCAL_PREF);
+      }
+      catch(e) {}
+      Services.prefs.setBoolPref(MARIONETTE_FORCELOCAL_PREF, marionette_forcelocal);
 
-  if (!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).
-    // 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);
-  }
-
-  let port = DEFAULT_PORT;
-  try {
-    port = Services.prefs.getIntPref(PORT_PREF);
-  } catch (e) {}
+      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).
+        // 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);
+      }
 
-  let s;
-  try {
-    Cu.import("chrome://marionette/content/server.js");
-    s = new MarionetteServer(port, forceLocal);
-    s.start();
-    this.logger.info(`Listening on port ${s.port}`);
-  } catch (e) {
-    this.logger.error(`Error on starting server: ${e}`);
-    dump(e.toString() + "\n" + e.stack + "\n");
-  } finally {
-    if (s) {
-      this.server = s;
+      let port;
+      try {
+        port = Services.prefs.getIntPref('marionette.defaultPrefs.port');
+      }
+      catch(e) {
+        port = 2828;
+      }
+      try {
+        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 + ': ' +
+                          e.fileName + " :: " + e.lineNumber);
+      }
     }
-  }
-};
+  },
 
-MarionetteComponent.prototype.uninit = function() {
-  if (!this.loaded_) {
-    return;
-  }
-  this.server.stop();
-  this.loaded_ = false;
+  uninit: function mc_uninit() {
+    if (this._marionetteServer) {
+      this._marionetteServer.closeListener();
+    }
+    this._loaded = false;
+  },
+
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MarionetteComponent]);
deleted file mode 100644
--- a/testing/marionette/dispatcher.js
+++ /dev/null
@@ -1,288 +0,0 @@
-/* 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";
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-
-Cu.import("chrome://marionette/content/command.js");
-Cu.import("chrome://marionette/content/emulator.js");
-Cu.import("chrome://marionette/content/error.js");
-Cu.import("chrome://marionette/content/driver.js");
-
-this.EXPORTED_SYMBOLS = ["Dispatcher"];
-
-const logger = Log.repository.getLogger("Marionette");
-const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
-
-/**
- * Manages a Marionette connection, and dispatches packets received to
- * their correct destinations.
- *
- * @param {number} connId
- *     Unique identifier of the connection this dispatcher should handle.
- * @param {DebuggerTransport} transport
- *     Debugger transport connection to the client.
- * @param {function(Emulator): GeckoDriver} driverFactory
- *     A factory function that takes an Emulator as argument and produces
- *     a GeckoDriver.
- * @param {function()} stopSignal
- *     Signal to stop the Marionette server.
- */
-this.Dispatcher = function(connId, transport, driverFactory, stopSignal) {
-  this.id = connId;
-  this.conn = transport;
-
-  // Marionette uses a protocol based on the debugger server, which
-  // requires passing back actor ID's with responses.  Unlike the debugger
-  // server, we don't actually have multiple actors, so just use a dummy
-  // value of "0".
-  this.actorId = "0";
-
-  // callback for when connection is closed
-  this.onclose = null;
-
-  // transport hooks are Dispatcher.prototype.onPacket
-  // and Dispatcher.prototype.onClosed
-  this.conn.hooks = this;
-
-  this.emulator = new Emulator(msg => this.sendResponse(msg, -1));
-  this.driver = driverFactory(this.emulator);
-  this.commandProcessor = new CommandProcessor(this.driver);
-
-  this.stopSignal_ = stopSignal;
-};
-
-/**
- * Debugger transport callback that dispatches the request.
- * Request handlers defined in this.requests take presedence
- * over those defined in this.driver.commands.
- */
-Dispatcher.prototype.onPacket = function(packet) {
-  logger.debug(`${this.id} -> ${packet.toSource()}`);
-
-  if (this.requests && this.requests[packet.name]) {
-    this.requests[packet.name].bind(this)(packet);
-  } else {
-    let id = this.beginNewCommand();
-    let ok = this.sendOk.bind(this);
-    let send = this.send.bind(this);
-    this.commandProcessor.execute(packet, ok, send, id);
-  }
-};
-
-/**
- * Debugger transport callback that cleans up
- * after a connection is closed.
- */
-Dispatcher.prototype.onClosed = function(status) {
-  this.driver.sessionTearDown();
-  if (this.onclose) {
-    this.onclose(this);
-  }
-};
-
-// Dispatcher specific command handlers:
-
-Dispatcher.prototype.getMarionetteID = function() {
-  let id = this.beginNewCommand();
-  this.sendResponse({from: "root", id: this.actorId}, id);
-};
-
-Dispatcher.prototype.emulatorCmdResult = function(msg) {
-  switch (this.driver.context) {
-    case Context.CONTENT:
-      this.driver.sendAsync("emulatorCmdResult", msg);
-      break;
-    case Context.CHROME:
-      let cb = this.emulator.popCallback(msg.id);
-      if (!cb) {
-        return;
-      }
-      cb.result(msg);
-      break;
-  }
-};
-
-/**
- * Quits Firefox with the provided flags and tears down the current
- * session.
- */
-Dispatcher.prototype.quitApplication = function(msg) {
-  let id = this.beginNewCommand();
-
-  if (this.driver.appName != "Firefox") {
-    this.sendError({
-      "message": "In app initiated quit only supported on Firefox",
-      "status": 500
-    }, id);
-    return;
-  }
-
-  let flags = Ci.nsIAppStartup.eAttemptQuit;
-  for (let k of msg.parameters.flags) {
-    flags |= Ci.nsIAppStartup[k];
-  }
-
-  this.stopSignal_();
-  this.sendOk(id);
-
-  this.driver.sessionTearDown();
-  Services.startup.quit(flags);
-};
-
-// Convenience methods:
-
-Dispatcher.prototype.sayHello = function() {
-  let id = this.beginNewCommand();
-  let yo = {from: "root", applicationType: "gecko", traits: []};
-  this.sendResponse(yo, id);
-};
-
-Dispatcher.prototype.sendOk = function(cmdId) {
-  this.sendResponse({from: this.actorId, ok: true}, cmdId);
-};
-
-Dispatcher.prototype.sendError = function(err, cmdId) {
-  let packet = {
-    from: this.actorId,
-    status: err.status,
-    sessionId: this.driver.sessionId,
-    error: err
-  };
-  this.sendResponse(packet, cmdId);
-};
-
-/**
- * Marshals and sends message to either client or emulator based on the
- * provided {@code cmdId}.
- *
- * This routine produces a Marionette protocol packet, which is different
- * to a WebDriver protocol response in that it contains an extra key
- * {@code from} for the debugger transport actor ID.  It also replaces the
- * key {@code value} with {@code error} when {@code msg.status} isn't
- * {@code 0}.
- *
- * @param {Object} msg
- *     Object with the properties {@code value}, {@code status}, and
- *     {@code sessionId}.
- * @param {UUID} cmdId
- *     The unique identifier for the command the message is a response to.
- */
-Dispatcher.prototype.send = function(msg, cmdId) {
-  let packet = {
-    from: this.actorId,
-    value: msg.value,
-    status: msg.status,
-    sessionId: msg.sessionId,
-  };
-
-  if (typeof packet.value == "undefined") {
-    packet.value = null;
-  }
-
-  // the Marionette protocol sends errors using the "error"
-  // key instead of, as Selenium, "value"
-  if (!error.isSuccess(msg.status)) {
-    packet.error = packet.value;
-    delete packet.value;
-  }
-
-  this.sendResponse(packet, cmdId);
-};
-
-// Low-level methods:
-
-/**
- * Delegates message to client or emulator based on the provided
- * {@code cmdId}.  The message is sent over the debugger transport socket.
- *
- * The command ID is a unique identifier assigned to the client's request
- * that is used to distinguish the asynchronous responses.
- *
- * Whilst responses to commands are synchronous and must be sent in the
- * correct order, emulator callbacks are more transparent and can be sent
- * at any time.  These callbacks won't change the current command state.
- *
- * @param {Object} payload
- *     The payload to send.
- * @param {UUID} cmdId
- *     The unique identifier for this payload.  {@code -1} signifies
- *     that it's an emulator callback.
- */
-Dispatcher.prototype.sendResponse = function(payload, cmdId) {
-  if (emulator.isCallback(cmdId)) {
-    this.sendToEmulator(payload);
-  } else {
-    this.sendToClient(payload, cmdId);
-    this.commandId = null;
-  }
-};
-
-/**
- * Send message to emulator over the debugger transport socket.
- * Notably this skips out-of-sync command checks.
- */
-Dispatcher.prototype.sendToEmulator = function(payload) {
-  this.sendRaw("emulator", payload);
-};
-
-/**
- * Send given payload as-is to the connected client over the debugger
- * transport socket.
- *
- * If {@code cmdId} evaluates to false, the current command state isn't
- * set, or the response is out-of-sync, a warning is logged and this
- * routine will return (no-op).
- */
-Dispatcher.prototype.sendToClient = function(payload, cmdId) {
-  if (!cmdId) {
-    logger.warn("Got response with no command ID");
-    return;
-  } else if (this.commandId === null) {
-    logger.warn(`No current command, ignoring response: ${payload.toSource}`);
-    return;
-  } else if (this.isOutOfSync(cmdId)) {
-    logger.warn(`Ignoring out-of-sync response with command ID: ${cmdId}`);
-    return;
-  }
-  this.driver.responseCompleted();
-  this.sendRaw("client", payload);
-};
-
-/**
- * Sends payload as-is over debugger transport socket to client,
- * and logs it.
- */
-Dispatcher.prototype.sendRaw = function(dest, payload) {
-  logger.debug(`${this.id} ${dest} <- ${payload.toSource()}`);
-  this.conn.send(payload);
-};
-
-/**
- * Begins a new command by generating a unique identifier and assigning
- * it to the current command state {@code Dispatcher.prototype.commandId}.
- *
- * @return {UUID}
- *     The generated unique identifier for the current command.
- */
-Dispatcher.prototype.beginNewCommand = function() {
-  let uuid = uuidGen.generateUUID().toString();
-  this.commandId = uuid;
-  return uuid;
-};
-
-Dispatcher.prototype.isOutOfSync = function(cmdId) {
-  return this.commandId !== cmdId;
-};
-
-Dispatcher.prototype.requests = {
-  getMarionetteID: Dispatcher.prototype.getMarionetteID,
-  emulatorCmdResult: Dispatcher.prototype.emulatorCmdResult,
-  quitApplication: Dispatcher.prototype.quitApplication
-};
deleted file mode 100644
--- a/testing/marionette/driver.js
+++ /dev/null
@@ -1,3314 +0,0 @@
-/* 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";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
-    .getService(Ci.mozIJSSubScriptLoader);
-
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-this.DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
-
-XPCOMUtils.defineLazyServiceGetter(
-    this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager");
-
-Cu.import("chrome://marionette/content/actions.js");
-Cu.import("chrome://marionette/content/elements.js");
-Cu.import("chrome://marionette/content/emulator.js");
-Cu.import("chrome://marionette/content/error.js");
-Cu.import("chrome://marionette/content/modal.js");
-Cu.import("chrome://marionette/content/simpletest.js");
-
-loader.loadSubScript("chrome://marionette/content/common.js");
-
-// preserve this import order:
-let utils = {};
-loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
-loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils);
-loader.loadSubScript("chrome://marionette/content/atoms.js", utils);
-loader.loadSubScript("chrome://marionette/content/sendkeys.js", utils);
-loader.loadSubScript("chrome://marionette/content/frame-manager.js");
-
-this.EXPORTED_SYMBOLS = ["GeckoDriver", "Context"];
-
-const FRAME_SCRIPT = "chrome://marionette/content/listener.js";
-const BROWSER_STARTUP_FINISHED = "browser-delayed-startup-finished";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-const SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
-const CLICK_TO_START_PREF = "marionette.debugging.clicktostart";
-const CONTENT_LISTENER_PREF = "marionette.contentListener";
-
-const logger = Log.repository.getLogger("Marionette");
-const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
-const globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"]
-    .getService(Ci.nsIMessageBroadcaster);
-let specialpowers = {};
-
-// This is used to prevent newSession from returning before the telephony
-// 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);
-
-// This is used on desktop to prevent newSession from returning before a page
-// load initiated by the Firefox command line has completed.
-let delayedBrowserStarted = false;
-Services.obs.addObserver(function () {
-  delayedBrowserStarted = true;
-}, BROWSER_STARTUP_FINISHED, false);
-
-this.Context = {
-  CHROME: "chrome",
-  CONTENT: "content",
-};
-
-this.Context.fromString = function(s) {
-  s = s.toUpperCase();
-  if (s in this) {
-    return this[s];
-  }
-  return null;
-};
-
-/**
- * Creates a transparent interface between the chrome- and content
- * processes.
- *
- * Calls to this object will  be proxied via the message manager to the active
- * browsing context (content) and responses will be provided back as
- * promises.
- *
- * The argument sequence is serialised and passed as an array, unless it
- * consists of a single object type that isn't null, in which case it's
- * passed literally.  The latter specialisation is temporary to achieve
- * backwards compatibility with listener.js.
- *
- * @param {function(): nsIMessageManager} mmFn
- *     Function returning the current message manager.
- * @param {function(string, Object, number)} sendAsyncFn
- *     Callback for sending async messages to the current listener.
- * @param {function(): BrowserObj} curBrowserFn
- *     Function that returns the current browser.
- */
-let ListenerProxy = function(mmFn, sendAsyncFn, curBrowserFn) {
-  this.curCmdId = null;
-  this.sendAsync = sendAsyncFn;
-
-  this.mmFn_ = mmFn;
-  this.curBrowserFn_ = curBrowserFn;
-};
-
-Object.defineProperty(ListenerProxy.prototype, "mm", {
-  get: function() { return this.mmFn_(); }
-});
-
-Object.defineProperty(ListenerProxy.prototype, "curBrowser", {
-  get: function() { return this.curBrowserFn_(); }
-});
-
-ListenerProxy.prototype.__noSuchMethod__ = function*(name, args) {
-  const ok = "Marionette:ok";
-  const val = "Marionette:done";
-  const err = "Marionette:error";
-
-  let proxy = new Promise((resolve, reject) => {
-    let removeListeners = (name, fn) => {
-      let rmFn = msg => {
-        if (this.isOutOfSync(msg.json.command_id)) {
-          logger.warn("Skipping out-of-sync response from listener: " +
-              msg.name + msg.json.toSource());
-          return;
-        }
-
-        listeners.remove();
-        modal.removeHandler(handleDialog);
-
-        fn(msg);
-        this.curCmdId = null;
-      };
-
-      listeners.push([name, rmFn]);
-      return rmFn;
-    };
-
-    let listeners = [];
-    listeners.add = () => {
-      this.mm.addMessageListener(ok, removeListeners(ok, okListener));
-      this.mm.addMessageListener(val, removeListeners(val, valListener));
-      this.mm.addMessageListener(err, removeListeners(err, errListener));
-    };
-    listeners.remove = () =>
-        listeners.map(l => this.mm.removeMessageListener(l[0], l[1]));
-
-    let okListener = () => resolve();
-    let valListener = msg => resolve(msg.json.value);
-    let errListener = msg => reject(
-        "error" in msg.objects ? msg.objects.error : msg.json);
-
-    let handleDialog = function(subject, topic) {
-      listeners.remove();
-      modal.removeHandler(handleDialog);
-      this.sendAsync("cancelRequest");
-      resolve();
-    }.bind(this);
-
-    // start content process listeners, and install observers for global-
-    // and tab modal dialogues
-    listeners.add();
-    modal.addHandler(handleDialog);
-
-    // convert to array if passed arguments
-    let msg;
-    if (args.length == 1 && typeof args[0] == "object" && args[0] !== null) {
-      msg = args[0];
-    } else {
-      msg = Array.prototype.slice.call(args);
-    }
-
-    this.sendAsync(name, msg, this.curCmdId);
-  });
-
-  return proxy;
-};
-
-ListenerProxy.prototype.isOutOfSync = function(id) {
-  return this.curCmdId !== id;
-};
-
-/**
- * Implements (parts of) the W3C WebDriver protocol.  GeckoDriver lives
- * in the chrome context and mediates content calls to the listener via
- * ListenerProxy.
- *
- * Throughout this prototype, functions with the argument {@code cmd}'s
- * documentation refers to the contents of the {@code cmd.parameters}
- * object.
- *
- * @param {string} appName
- *     Description of the product, for example "B2G" or "Firefox".
- * @param {string} device
- *     Device this driver should assume.
- * @param {Emulator=} emulator
- *     Reference to the emulator connection, if running on an emulator.
- */
-this.GeckoDriver = function(appName, device, emulator) {
-  this.appName = appName;
-  this.emulator = emulator;
-
-  this.sessionId = null;
-  // holds list of BrowserObjs
-  this.browsers = {};
-  // points to current browser
-  this.curBrowser = null;
-  this.context = Context.CONTENT;
-  this.scriptTimeout = null;
-  this.searchTimeout = null;
-  this.pageTimeout = null;
-  this.timer = null;
-  this.inactivityTimer = null;
-  // called by simpletest methods
-  this.heartbeatCallback = function() {};
-  this.marionetteLog = new MarionetteLogObj();
-  // topmost chrome frame
-  this.mainFrame = null;
-  // chrome iframe that currently has focus
-  this.curFrame = null;
-  this.mainContentFrameId = null;
-  this.importedScripts = FileUtils.getFile("TmpD", ["marionetteChromeScripts"]);
-  this.importedScriptHashes = {};
-  this.importedScriptHashes[Context.CONTENT] = [];
-  this.importedScriptHashes[Context.CHROME] = [];
-  this.currentFrameElement = null;
-  this.testName = null;
-  this.mozBrowserClose = null;
-  this.enabled_security_pref = false;
-  this.sandbox = null;
-  // frame ID of the current remote frame, used for mozbrowserclose events
-  this.oopFrameId = null;
-  this.observing = null;
-  this._browserIds = new WeakMap();
-  this.actions = new ActionChain(utils);
-
-  this.sessionCapabilities = {
-    // Mandated capabilities
-    "browserName": this.appName,
-    "browserVersion": Services.appinfo.version,
-    "platformName": Services.appinfo.OS.toUpperCase(),
-    "platformVersion": Services.appinfo.platformVersion,
-
-    // Supported features
-    "handlesAlerts": false,
-    "nativeEvents": false,
-    "raisesAccessibilityExceptions": false,
-    "rotatable": this.appName == "B2G",
-    "secureSsl": false,
-    "takesElementScreenshot": true,
-    "takesScreenshot": true,
-
-    // Selenium 2 compat
-    "platform": Services.appinfo.OS.toUpperCase(),
-
-    // Proprietary extensions
-    "XULappId" : Services.appinfo.ID,
-    "appBuildId" : Services.appinfo.appBuildID,
-    "device": device,
-    "version": Services.appinfo.version
-  };
-
-  this.mm = globalMessageManager;
-  this.listener = new ListenerProxy(
-      () => this.mm,
-      this.sendAsync.bind(this),
-      () => this.curBrowser);
-
-  this.dialog = null;
-  let handleDialog = (subject, topic) => {
-    let winr;
-    if (topic == modal.COMMON_DIALOG_LOADED) {
-      winr = Cu.getWeakReference(subject);
-    }
-    this.dialog = new modal.Dialog(() => this.curBrowser, winr);
-  };
-  modal.addHandler(handleDialog);
-};
-
-GeckoDriver.prototype.QueryInterface = XPCOMUtils.generateQI([
-  Ci.nsIMessageListener,
-  Ci.nsIObserver,
-  Ci.nsISupportsWeakReference
-]);
-
-/**
- * Switches to the global ChromeMessageBroadcaster, potentially replacing
- * a frame-specific ChromeMessageSender.  Has no effect if the global
- * ChromeMessageBroadcaster is already in use.  If this replaces a
- * frame-specific ChromeMessageSender, it removes the message listeners
- * from that sender, and then puts the corresponding frame script "to
- * sleep", which removes most of the message listeners from it as well.
- */
-GeckoDriver.prototype.switchToGlobalMessageManager = function() {
-  if (this.curBrowser && this.curBrowser.frameManager.currentRemoteFrame !== null) {
-    this.curBrowser.frameManager.removeMessageManagerListeners(this.mm);
-    this.sendAsync("sleepSession");
-    this.curBrowser.frameManager.currentRemoteFrame = null;
-  }
-  this.mm = globalMessageManager;
-};
-
-/**
- * Helper method to send async messages to the content listener.
- * Correct usage is to pass in the name of a function in listener.js,
- * a message object consisting of JSON serialisable primitives,
- * and the current command's ID.
- *
- * @param {string} name
- *     Suffix of the targetted message listener ({@code Marionette:<suffix>}).
- * @param {Object=} msg
- *     JSON serialisable object to send to the listener.
- * @param {number=} cmdId
- *     Command ID to ensure synchronisity.
- */
-GeckoDriver.prototype.sendAsync = function(name, msg, cmdId) {
-  let curRemoteFrame = this.curBrowser.frameManager.currentRemoteFrame;
-  name = `Marionette:${name}`;
-
-  if (cmdId) {
-    msg.command_id = cmdId;
-  }
-
-  if (curRemoteFrame === null) {
-    this.curBrowser.executeWhenReady(() => {
-      this.mm.broadcastAsyncMessage(name + this.curBrowser.curFrameId, msg);
-    });
-  } else {
-    let remoteFrameId = curRemoteFrame.targetFrameId;
-    try {
-      this.mm.sendAsyncMessage(name + remoteFrameId, msg);
-    } catch (e) {
-      switch(e.result) {
-        case Components.results.NS_ERROR_FAILURE:
-          throw new FrameSendFailureError(curRemoteFrame);
-        case Components.results.NS_ERROR_NOT_INITIALIZED:
-          throw new FrameSendNotInitializedError(curRemoteFrame);
-        default:
-          throw new WebDriverError(e.toString());
-      }
-    }
-  }
-};
-
-/**
- * Gets the current active window.
- *
- * @return {nsIDOMWindow}
- */
-GeckoDriver.prototype.getCurrentWindow = function() {
-  let typ = null;
-  if (this.curFrame == null) {
-    if (this.curBrowser == null) {
-      if (this.context == Context.CONTENT) {
-        typ = 'navigator:browser';
-      }
-      return Services.wm.getMostRecentWindow(typ);
-    } else {
-      return this.curBrowser.window;
-    }
-  } else {
-    return this.curFrame;
-  }
-};
-
-/**
- * Gets the the window enumerator.
- *
- * @return {nsISimpleEnumerator}
- */
-GeckoDriver.prototype.getWinEnumerator = function() {
-  let typ = null;
-  if (this.appName != "B2G" && this.context == Context.CONTENT) {
-    typ = "navigator:browser";
-  }
-  return Services.wm.getEnumerator(typ);
-};
-
-GeckoDriver.prototype.addFrameCloseListener = function(action) {
-  let win = this.getCurrentWindow();
-  this.mozBrowserClose = e => {
-    if (e.target.id == this.oopFrameId) {
-      win.removeEventListener("mozbrowserclose", this.mozBrowserClose, true);
-      this.switchToGlobalMessageManager();
-      throw new FrameSendFailureError(
-          `The frame closed during the ${action}, recovering to allow further communications`);
-    }
-  };
-  win.addEventListener("mozbrowserclose", this.mozBrowserClose, true);
-};
-
-/**
- * Create a new BrowserObj for window and add to known browsers.
- *
- * @param {nsIDOMWindow} win
- *     Window for which we will create a BrowserObj.
- *
- * @return {string}
- *     Returns the unique server-assigned ID of the window.
- */
-GeckoDriver.prototype.addBrowser = function(win) {
-  let browser = new BrowserObj(win, this);
-  let winId = win.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
-  winId = winId + ((this.appName == "B2G") ? "-b2g" : "");
-  this.browsers[winId] = browser;
-  this.curBrowser = this.browsers[winId];
-  if (typeof this.curBrowser.elementManager.seenItems[winId] == "undefined") {
-    // add this to seenItems so we can guarantee
-    // the user will get winId as this window's id
-    this.curBrowser.elementManager.seenItems[winId] = Cu.getWeakReference(win);
-  }
-};
-
-/**
- * Registers a new browser, win, with Marionette.
- *
- * If we have not seen the browser content window before, the listener
- * frame script will be loaded into it.  If isNewSession is true, we will
- * switch focus to the start frame when it registers.
- *
- * @param {nsIDOMWindow} win
- *     Window whose browser we need to access.
- * @param {boolean=false} isNewSession
- *     True if this is the first time we're talking to this browser.
- */
-GeckoDriver.prototype.startBrowser = function(win, isNewSession=false) {
-  this.mainFrame = win;
-  this.curFrame = null;
-  this.addBrowser(win);
-  this.curBrowser.isNewSession = isNewSession;
-  this.curBrowser.startSession(isNewSession, win, this.whenBrowserStarted.bind(this));
-};
-
-/**
- * Callback invoked after a new session has been started in a browser.
- * Loads the Marionette frame script into the browser if needed.
- *
- * @param {nsIDOMWindow} win
- *     Window whose browser we need to access.
- * @param {boolean} isNewSession
- *     True if this is the first time we're talking to this browser.
- */
-GeckoDriver.prototype.whenBrowserStarted = function(win, isNewSession) {
-  utils.window = win;
-
-  try {
-    let mm = win.window.messageManager;
-    if (!isNewSession) {
-      // Loading the frame script corresponds to a situation we need to
-      // return to the server. If the messageManager is a message broadcaster
-      // with no children, we don't have a hope of coming back from this call,
-      // so send the ack here. Otherwise, make a note of how many child scripts
-      // will be loaded so we known when it's safe to return.
-      if (mm.childCount != 0) {
-        this.curBrowser.frameRegsPending = mm.childCount;
-      }
-    }
-
-    if (!Services.prefs.getBoolPref("marionette.contentListener") || !isNewSession) {
-      mm.loadFrameScript(FRAME_SCRIPT, true, true);
-      Services.prefs.setBoolPref("marionette.contentListener", true);
-    }
-  } catch (e) {
-    // there may not always be a content process
-    logger.error(
-        `Could not load listener into content for page ${win.location.href}: ${e}`);
-  }
-};
-
-/**
- * Recursively get all labeled text.
- *
- * @param {nsIDOMElement} el
- *     The parent element.
- * @param {Array.<string>} lines
- *      Array that holds the text lines.
- */
-GeckoDriver.prototype.getVisibleText = function(el, lines) {
-  try {
-    if (utils.isElementDisplayed(el)) {
-      if (el.value) {
-        lines.push(el.value);
-      }
-      for (let child in el.childNodes) {
-        this.getVisibleText(el.childNodes[child], lines);
-      }
-    }
-  } catch (e) {
-    if (el.nodeName == "#text") {
-      lines.push(el.textContent);
-    }
-  }
-};
-
-/**
-  * Given a file name, this will delete the file from the temp directory
-  * if it exists.
-  *
-  * @param {string} filename
-  */
-GeckoDriver.prototype.deleteFile = function(filename) {
-  let file = FileUtils.getFile("TmpD", [filename.toString()]);
-  if (file.exists()) {
-    file.remove(true);
-  }
-};
-
-/**
- * Handles registration of new content listener browsers.  Depending on
- * their type they are either accepted or ignored.
- */
-GeckoDriver.prototype.registerBrowser = function(id, be) {
-  let nullPrevious = this.curBrowser.curFrameId == null;
-  let listenerWindow = Services.wm.getOuterWindowWithId(id);
-
-  // go in here if we're already in a remote frame
-  if (this.curBrowser.frameManager.currentRemoteFrame !== null &&
-      (!listenerWindow || this.mm == this.curBrowser.frameManager
-          .currentRemoteFrame.messageManager.get())) {
-    // The outerWindowID from an OOP frame will not be meaningful to
-    // the parent process here, since each process maintains its own
-    // independent window list.  So, it will either be null (!listenerWindow)
-    // if we're already in a remote frame, or it will point to some
-    // random window, which will hopefully cause an href mismatch.
-    // Currently this only happens in B2G for OOP frames registered in
-    // Marionette:switchToFrame, so we'll acknowledge the switchToFrame
-    // message here.
-    //
-    // TODO: Should have a better way of determining that this message
-    // is from a remote frame.
-    this.curBrowser.frameManager.currentRemoteFrame.targetFrameId =
-        this.generateFrameId(id);
-  }
-
-  let reg = {};
-  // this will be sent to tell the content process if it is the main content
-  let mainContent = this.curBrowser.mainContentId == null;
-  if (be.getAttribute("type") != "content") {
-    // curBrowser holds all the registered frames in knownFrames
-    let uid = this.generateFrameId(id);
-    reg.id = uid;
-    reg.remotenessChange = this.curBrowser.register(uid, be);
-  }
-
-  // set to true if we updated mainContentId
-  mainContent = mainContent == true &&
-      this.curBrowser.mainContentId != null;
-  if (mainContent) {
-    this.mainContentFrameId = this.curBrowser.curFrameId;
-  }
-
-  this.curBrowser.elementManager.seenItems[reg.id] =
-      Cu.getWeakReference(listenerWindow);
-  if (nullPrevious && (this.curBrowser.curFrameId != null)) {
-    this.sendAsync("newSession",
-        {
-          B2G: (this.appName == "B2G"),
-          raisesAccessibilityExceptions:
-              this.sessionCapabilities.raisesAccessibilityExceptions
-        },
-        this.newSessionCommandId);
-    if (this.curBrowser.isNewSession) {
-      this.newSessionCommandId = null;
-    }
-  }
-
-  return [reg, mainContent];
-};
-
-GeckoDriver.prototype.registerPromise = function() {
-  const li = "Marionette:register";
-
-  return new Promise((resolve) => {
-    this.mm.addMessageListener(li, function cb(msg) {
-      let wid = msg.json.value;
-      let be = msg.target;
-      let rv = this.registerBrowser(wid, be);
-
-      if (this.curBrowser.frameRegsPending > 0) {
-        this.curBrowser.frameRegsPending--;
-      }
-
-      if (this.curBrowser.frameRegsPending == 0) {
-        this.mm.removeMessageListener(li, cb);
-        resolve();
-      }
-
-      // this is a sync message and listeners expect the ID back
-      return rv;
-    }.bind(this));
-  });
-};
-
-GeckoDriver.prototype.listeningPromise = function() {
-  const li = "Marionette:listenersAttached";
-  return new Promise((resolve) => {
-    this.mm.addMessageListener(li, function() {
-      this.mm.removeMessageListener(li, this);
-      resolve();
-    }.bind(this));
-  });
-};
-
-/** Create a new session. */
-GeckoDriver.prototype.newSession = function(cmd, resp) {
-  this.sessionId = cmd.parameters.sessionId ||
-      cmd.parameters.session_id ||
-      uuidGen.generateUUID().toString();
-
-  this.newSessionCommandId = cmd.id;
-  this.setSessionCapabilities(cmd.parameters.capabilities);
-  this.scriptTimeout = 10000;
-
-  // SpecialPowers requires insecure automation-only features that we
-  // put behind a pref
-  let sec = false;
-  try {
-    sec = Services.prefs.getBoolPref(SECURITY_PREF);
-  } catch (e) {}
-  if (!sec) {
-    this.enabled_security_pref = true;
-    Services.prefs.setBoolPref(SECURITY_PREF, true);
-  }
-
-  if (!specialpowers.hasOwnProperty("specialPowersObserver")) {
-    loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.js",
-        specialpowers);
-    specialpowers.specialPowersObserver = new specialpowers.SpecialPowersObserver();
-    specialpowers.specialPowersObserver.init();
-    specialpowers.specialPowersObserver._loadFrameScript();
-  }
-
-  let registerBrowsers = this.registerPromise();
-  let browserListening = this.listeningPromise();
-
-  let waitForWindow = function() {
-    let win = this.getCurrentWindow();
-    if (!win) {
-      // if the window isn't even created, just poll wait for it
-      let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      checkTimer.initWithCallback(waitForWindow.bind(this), 100,
-          Ci.nsITimer.TYPE_ONE_SHOT);
-    } else if (win.document.readyState != "complete") {
-      // otherwise, wait for it to be fully loaded before proceeding
-      let listener = ev => {
-        // ensure that we proceed, on the top level document load event
-        // (not an iframe one...)
-        if (ev.target != win.document) {
-          return;
-        }
-        win.removeEventListener("load", listener);
-        waitForWindow.call(this);
-      };
-      win.addEventListener("load", listener, true);
-    } else {
-      let clickToStart;
-      try {
-        clickToStart = Services.prefs.getBoolPref(CLICK_TO_START_PREF);
-      } catch (e) {}
-      if (clickToStart && (this.appName != "B2G")) {
-        let pService = Cc["@mozilla.org/embedcomp/prompt-service;1"]
-            .getService(Ci.nsIPromptService);
-        pService.alert(win, "", "Click to start execution of marionette tests");
-      }
-      this.startBrowser(win, true);
-    }
-  };
-
-  let runSessionStart = function() {
-    if (!Services.prefs.getBoolPref(CONTENT_LISTENER_PREF)) {
-      waitForWindow.call(this);
-    } else if (this.appName != "Firefox" && this.curBrowser === null) {
-      // if there is a content listener, then we just wake it up
-      this.addBrowser(this.getCurrentWindow());
-      this.curBrowser.startSession(this.whenBrowserStarted.bind(this));
-      this.mm.broadcastAsyncMessage("Marionette:restart", {});
-    } else {
-      throw new WebDriverError("Session already running");
-    }
-    this.switchToGlobalMessageManager();
-  };
-
-  if (!delayedBrowserStarted && this.appName != "B2G") {
-    let self = this;
-    Services.obs.addObserver(function onStart() {
-      Services.obs.removeObserver(onStart, BROWSER_STARTUP_FINISHED);
-      runSessionStart.call(self);
-    }, BROWSER_STARTUP_FINISHED, false);
-  } else {
-    runSessionStart.call(this);
-  }
-
-  yield registerBrowsers;
-  yield browserListening;
-
-  resp.sessionId = this.sessionId;
-  resp.value = this.sessionCapabilities;
-};
-
-/**
- * Send the current session's capabilities to the client.
- *
- * Capabilities informs the client of which WebDriver features are
- * supported by Firefox and Marionette.  They are immutable for the
- * length of the session.
- *
- * The return value is an immutable map of string keys
- * ("capabilities") to values, which may be of types boolean,
- * numerical or string.
- */
-GeckoDriver.prototype.getSessionCapabilities = function(cmd, resp) {
-  resp.value = this.sessionCapabilities;
-};
-
-/**
- * Update the sessionCapabilities object with the keys that have been
- * passed in when a new session is created.
- *
- * This part of the WebDriver spec is currently in flux, see
- * http://lists.w3.org/Archives/Public/public-browser-tools-testing/2014OctDec/0000.html
- *
- * This is not a public API, only available when a new session is
- * created.
- *
- * @param {Object} newCaps
- *     Key/value dictionary to overwrite session's current capabilities.
- */
-GeckoDriver.prototype.setSessionCapabilities = function(newCaps) {
-  const copy = (from, to={}) => {
-    let errors = {};
-
-    for (let key in from) {
-      if (key === "desiredCapabilities") {
-        // Keeping desired capabilities separate for now so that we can keep
-        // backwards compatibility
-        to = copy(from[key], to);
-      } else if (key === "requiredCapabilities") {
-        for (let caps in from[key]) {
-          if (from[key][caps] !== this.sessionCapabilities[caps]) {
-            errors[caps] = from[key][caps] + " does not equal " +
-                this.sessionCapabilities[caps];
-          }
-        }
-      }
-      to[key] = from[key];
-    }
-
-    if (Object.keys(errors).length == 0) {
-      return to;
-    }
-
-    throw new SessionNotCreatedError(
-        `Not all requiredCapabilities could be met: ${JSON.stringify(errors)}`);
-  };
-
-  // clone, overwrite, and set
-  let caps = copy(this.sessionCapabilities);
-  caps = copy(newCaps, caps);
-  this.sessionCapabilities = caps;
-};
-
-/**
- * Log message.  Accepts user defined log-level.
- *
- * @param {string} value
- *     Log message.
- * @param {string} level
- *     Arbitrary log level.
- */
-GeckoDriver.prototype.log = function(cmd, resp) {
-  this.marionetteLog.log(cmd.parameters.value, cmd.parameters.level);
-};
-
-/** Return all logged messages. */
-GeckoDriver.prototype.getLogs = function(cmd, resp) {
-  resp.value = this.marionetteLog.getLogs();
-};
-
-/**
- * Sets the context of the subsequent commands to be either "chrome" or
- * "content".
- *
- * @param {string} value
- *     Name of the context to be switched to.  Must be one of "chrome" or
- *     "content".
- */
-GeckoDriver.prototype.setContext = function(cmd, resp) {
-  let val = cmd.parameters.value;
-  let ctx = Context.fromString(val);
-  if (ctx === null) {
-    throw new WebDriverError(`Invalid context: ${val}`);
-  }
-  this.context = ctx;
-};
-
-/** Gets the context of the server, either "chrome" or "content". */
-GeckoDriver.prototype.getContext = function(cmd, resp) {
-  resp.value = this.context.toString();
-};
-
-/**
- * Returns a chrome sandbox that can be used by the execute and
- * executeWithCallback functions.
- *
- * @param {nsIDOMWindow} win
- *     Window in which we will execute code.
- * @param {Marionette} mn
- *     Marionette test instance.
- * @param {Object} args
- *     Arguments given by client.
- * @param {boolean} sp
- *     True to enable special powers in the sandbox, false not to.
- *
- * @return {nsIXPCComponents_utils_Sandbox}
- *     Returns the sandbox.
- */
-GeckoDriver.prototype.createExecuteSandbox = function(win, mn, sp) {
-  let sb = new Cu.Sandbox(win,
-      {sandboxPrototype: win, wantXrays: false, sandboxName: ""});
-  sb.global = sb;
-  sb.testUtils = utils;
-
-  mn.exports.forEach(function(fn) {
-    try {
-      sb[fn] = mn[fn].bind(mn);
-    } catch(e) {
-      sb[fn] = mn[fn];
-    }
-  });
-
-  sb.isSystemMessageListenerReady = () => systemMessageListenerReady;
-
-  if (sp) {
-    let pow = [
-      "chrome://specialpowers/content/specialpowersAPI.js",
-      "chrome://specialpowers/content/SpecialPowersObserverAPI.js",
-      "chrome://specialpowers/content/ChromePowers.js",
-    ];
-    pow.map(s => loader.loadSubScript(s, sb));
-  }
-
-  return sb;
-};
-
-/**
- * Apply arguments sent from the client to the current (possibly reused)
- * execution sandbox.
- */
-GeckoDriver.prototype.applyArgumentsToSandbox = function(win, sb, args) {
-  sb.__marionetteParams = this.curBrowser.elementManager.convertWrappedArguments(args, win);
-  sb.__namedArgs = this.curBrowser.elementManager.applyNamedArgs(args);
-};
-
-/**
- * Executes a script in the given sandbox.
- *
- * @param {Response} resp
- *     Response object given to the command calling this routine.
- * @param {nsIXPCComponents_utils_Sandbox} sandbox
- *     Sandbox in which the script will run.
- * @param {string} script
- *     Script to run.
- * @param {boolean} directInject
- *     If true, then the script will be run as is, and not as a function
- *     body (as you would do using the WebDriver spec).
- * @param {boolean} async
- *     True if the script is asynchronous.
- * @param {number} timeout
- *     When to interrupt script in milliseconds.
- */
-GeckoDriver.prototype.executeScriptInSandbox = function(
-    resp,
-    sandbox,
-    script,
-    directInject,
-    async,
-    timeout) {
-  if (directInject && async && (timeout == null || timeout == 0))
-    throw new TimeoutError("Please set a timeout");
-
-  if (this.importedScripts.exists()) {
-    let stream = Cc["@mozilla.org/network/file-input-stream;1"]
-        .createInstance(Ci.nsIFileInputStream);
-    stream.init(this.importedScripts, -1, 0, 0);
-    let data = NetUtil.readInputStreamToString(stream, stream.available());
-    stream.close();
-    script = data + script;
-  }
-
-  let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file", 0);
-
-  if (directInject && !async &&
-      (res == undefined || res.passed == undefined)) {
-    throw new WebDriverError("finish() not called");
-  }
-
-  if (!async) {
-    // It's fine to pass on and modify resp here because
-    // executeScriptInSandbox is the last function to be called
-    // in execute and executeWithCallback respectively.
-    resp.value = this.curBrowser.elementManager.wrapValue(res);
-  }
-};
-
-/**
- * Execute the given script either as a function body or directly (for
- * mochitest-like JS Marionette tests).
- *
- * If directInject is ture, it will run directly and not as a function
- * body.
- */
-GeckoDriver.prototype.execute = function(cmd, resp, directInject) {
-  let {inactivityTimeout,
-       scriptTimeout,
-       script,
-       newSandbox,
-       args,
-       specialPowers,
-       filename,
-       line} = cmd.parameters;
-
-  if (!scriptTimeout) {
-    scriptTimeout = this.scriptTimeout;
-  }
-  if (typeof newSandbox == "undefined") {
-    newSandbox = true;
-  }
-
-  if (this.context == Context.CONTENT) {
-    resp.value = yield this.listener.executeScript({
-      script: script,
-      args: args,
-      newSandbox: newSandbox,
-      timeout: scriptTimeout,
-      specialPowers: specialPowers,
-      filename: filename,
-      line: line
-    });
-    return;
-  }
-
-  // handle the inactivity timeout
-  let that = this;
-  if (inactivityTimeout) {
-    let setTimer = function() {
-      that.inactivityTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      if (that.inactivityTimer != null) {
-        that.inactivityTimer.initWithCallback(function() {
-          throw new ScriptTimeoutError("timed out due to inactivity");
-        }, inactivityTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
-      }
-    };
-    setTimer();
-    this.heartbeatCallback = function() {
-      that.inactivityTimer.cancel();
-      setTimer();
-    };
-  }
-
-  let win = this.getCurrentWindow();
-  if (!this.sandbox || newSandbox) {
-    let marionette = new Marionette(
-        this,
-        win,
-        "chrome",
-        this.marionetteLog,
-        scriptTimeout,
-        this.heartbeatCallback,
-        this.testName);
-    this.sandbox = this.createExecuteSandbox(
-        win,
-        marionette,
-        specialPowers);
-    if (!this.sandbox) {
-      return;
-    }
-  }
-  this.applyArgumentsToSandbox(win, this.sandbox, args);
-
-  try {
-    this.sandbox.finish = () => {
-      if (this.inactivityTimer != null) {
-        this.inactivityTimer.cancel();
-      }
-      return this.sandbox.generate_results();
-    };
-
-    if (!directInject) {
-      script = `let func = function() { ${script} }; func.apply(null, __marionetteParams);`;
-    }
-    this.executeScriptInSandbox(
-        resp,
-        this.sandbox,
-        script,
-        directInject,
-        false /* async */,
-        scriptTimeout);
-  } catch (e) {
-    throw new JavaScriptError(e, "execute_script", filename, line, script);
-  }
-};
-
-/**
- * Set the timeout for asynchronous script execution.
- *
- * @param {number} ms
- *     Time in milliseconds.
- */
-GeckoDriver.prototype.setScriptTimeout = function(cmd, resp) {
-  let ms = parseInt(cmd.parameters.ms);
-  if (isNaN(ms)) {
-    throw new WebDriverError("Not a Number");
-  }
-  this.scriptTimeout = ms;
-};
-
-/**
- * Execute pure JavaScript.  Used to execute mochitest-like Marionette
- * tests.
- */
-GeckoDriver.prototype.executeJSScript = function(cmd, resp) {
-  // TODO(ato): cmd.newSandbox doesn't ever exist?
-  // All pure JS scripts will need to call
-  // Marionette.finish() to complete the test
-  if (typeof cmd.newSandbox == "undefined") {
-    // If client does not send a value in newSandbox,
-    // then they expect the same behaviour as WebDriver.
-    cmd.newSandbox = true;
-  }
-
-  switch (this.context) {
-    case Context.CHROME:
-      if (cmd.parameters.async) {
-        yield this.executeWithCallback(cmd, resp, cmd.parameters.async);
-      } else {
-        this.execute(cmd, resp, true /* async */);
-      }
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.executeJSScript({
-        script: cmd.parameters.script,
-        args: cmd.parameters.args,
-        newSandbox: cmd.parameters.newSandbox,
-        async: cmd.parameters.async,
-        timeout: cmd.parameters.scriptTimeout ?
-            cmd.parameters.scriptTimeout : this.scriptTimeout,
-        inactivityTimeout: cmd.parameters.inactivityTimeout,
-        specialPowers: cmd.parameters.specialPowers,
-        filename: cmd.parameters.filename,
-        line: cmd.parameters.line,
-      });
-      break;
- }
-};
-
-/**
- * This function is used by executeAsync and executeJSScript to execute
- * a script in a sandbox.
- *
- * For executeJSScript, it will return a message only when the finish()
- * method is called.
- *
- * For executeAsync, it will return a response when
- * {@code marionetteScriptFinished} (equivalent to
- * {@code arguments[arguments.length-1]}) function is called,
- * or if it times out.
- *
- * If directInject is true, it will be run directly and not as a
- * function body.
- */
-GeckoDriver.prototype.executeWithCallback = function(cmd, resp, directInject) {
-  let {script,
-      args,
-      newSandbox,
-      inactivityTimeout,
-      scriptTimeout,
-      specialPowers,
-      filename,
-      line} = cmd.parameters;
-
-  if (!scriptTimeout) {
-    scriptTimeout = this.scriptTimeout;
-  }
-  if (typeof newSandbox == "undefined") {
-    newSandbox = true;
-  }
-
-  if (this.context == Context.CONTENT) {
-    resp.value = yield this.listener.executeAsyncScript({
-      script: script,
-      args: args,
-      id: cmd.id,
-      newSandbox: newSandbox,
-      timeout: scriptTimeout,
-      inactivityTimeout: inactivityTimeout,
-      specialPowers: specialPowers,
-      filename: filename,
-      line: line
-    });
-    return;
-  }
-
-  // handle the inactivity timeout
-  let that = this;
-  if (inactivityTimeout) {
-    this.inactivityTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    if (this.inactivityTimer != null) {
-      this.inactivityTimer.initWithCallback(function() {
-       chromeAsyncReturnFunc(new ScriptTimeoutError("timed out due to inactivity"));
-      }, inactivityTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
-    }
-    this.heartbeatCallback = function resetInactivityTimer() {
-      that.inactivityTimer.cancel();
-      that.inactivityTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      if (that.inactivityTimer != null) {
-        that.inactivityTimer.initWithCallback(function() {
-          chromeAsyncReturnFunc(new ScriptTimeoutError("timed out due to inactivity"));
-        }, inactivityTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
-      }
-    };
-  }
-
-  let win = this.getCurrentWindow();
-  let origOnError = win.onerror;
-  that.timeout = scriptTimeout;
-
-  let res = yield new Promise(function(resolve, reject) {
-    let chromeAsyncReturnFunc = function(val) {
-      if (that.emulator.cbs.length > 0) {
-        that.emulator.cbs = [];
-        throw new WebDriverError("Emulator callback still pending when finish() called");
-      }
-
-      if (cmd.id == that.sandbox.command_id) {
-        if (that.timer != null) {
-          that.timer.cancel();
-          that.timer = null;
-        }
-
-        win.onerror = origOnError;
-
-        if (error.isError(val)) {
-          reject(val);
-        } else {
-          resolve(val);
-        }
-      }
-
-      if (that.inactivityTimer != null) {
-        that.inactivityTimer.cancel();
-      }
-    };
-
-    let chromeAsyncFinish = function() {
-      let res = that.sandbox.generate_results();
-      chromeAsyncReturnFunc(res);
-    };
-
-    let chromeAsyncError = function(e, func, file, line, script) {
-      let err = new JavaScriptError(e, func, file, line, script);
-      chromeAsyncReturnFunc(err);
-    };
-
-    if (!this.sandbox || newSandbox) {
-      let marionette = new Marionette(
-          this,
-          win,
-          "chrome",
-          this.marionetteLog,
-          scriptTimeout,
-          this.heartbeatCallback,
-          this.testName);
-      this.sandbox = this.createExecuteSandbox(win, marionette, specialPowers);
-      if (!this.sandbox) {
-        return;
-      }
-    }
-    this.sandbox.command_id = cmd.id;
-    this.sandbox.runEmulatorCmd = (cmd, cb) => {
-      let ecb = new EmulatorCallback();
-      ecb.onresult = cb;
-      ecb.onerror = chromeAsyncError;
-      this.emulator.pushCallback(ecb);
-      this.emulator.send({emulator_cmd: cmd, id: ecb.id});
-    };
-    this.sandbox.runEmulatorShell = (args, cb) => {
-      let ecb = new EmulatorCallback();
-      ecb.onresult = cb;
-      ecb.onerror = chromeAsyncError;
-      this.emulator.pushCallback(ecb);
-      this.emulator.send({emulator_shell: args, id: ecb.id});
-    };
-    this.applyArgumentsToSandbox(win, this.sandbox, args);
-
-    // NB: win.onerror is not hooked by default due to the inability to
-    // differentiate content exceptions from chrome exceptions. See bug
-    // 1128760 for more details. A debug_script flag can be set to
-    // reenable onerror hooking to help debug test scripts.
-    if (cmd.parameters.debug_script) {
-      win.onerror = function(msg, url, line) {
-        let err = new JavaScriptError(`${msg} at: ${url} line: ${line}`);
-        chromeAsyncReturnFunc(err);
-        return true;
-      };
-    }
-
-    try {
-      this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      if (this.timer != null) {
-        this.timer.initWithCallback(function() {
-          chromeAsyncReturnFunc(new ScriptTimeoutError("timed out"));
-        }, that.timeout, Ci.nsITimer.TYPE_ONE_SHOT);
-      }
-
-      this.sandbox.returnFunc = chromeAsyncReturnFunc;
-      this.sandbox.finish = chromeAsyncFinish;
-
-      if (!directInject) {
-        script =  "__marionetteParams.push(returnFunc);" +
-            "let marionetteScriptFinished = returnFunc;" +
-            "let __marionetteFunc = function() {" + script + "};" +
-            "__marionetteFunc.apply(null, __marionetteParams);";
-      }
-
-      this.executeScriptInSandbox(
-          resp,
-          this.sandbox,
-          script,
-          directInject,
-          true /* async */,
-          scriptTimeout);
-    } catch (e) {
-      chromeAsyncError(e, "execute_async_script", filename, line, script);
-    }
-  }.bind(this));
-
-  resp.value = that.curBrowser.elementManager.wrapValue(res);
-};
-
-/**
- * Navigate to to given URL.
- *
- * This will follow redirects issued by the server.  When the method
- * returns is based on the page load strategy that the user has
- * selected.
- *
- * Documents that contain a META tag with the "http-equiv" attribute
- * set to "refresh" will return if the timeout is greater than 1
- * second and the other criteria for determining whether a page is
- * loaded are met.  When the refresh period is 1 second or less and
- * the page load strategy is "normal" or "conservative", it will
- * wait for the page to complete loading before returning.
- *
- * If any modal dialog box, such as those opened on
- * window.onbeforeunload or window.alert, is opened at any point in
- * the page load, it will return immediately.
- *
- * If a 401 response is seen by the browser, it will return
- * immediately.  That is, if BASIC, DIGEST, NTLM or similar
- * authentication is required, the page load is assumed to be
- * complete.  This does not include FORM-based authentication.
- *
- * @param {string} url
- *     URL to navigate to.
- */
-GeckoDriver.prototype.get = function(cmd, resp) {
-  let url = cmd.parameters.url;
-
-  switch (this.context) {
-    case Context.CONTENT:
-      // If a remoteness update interrupts our page load, this will never return
-      // We need to re-issue this request to correctly poll for readyState and
-      // send errors.
-      this.curBrowser.pendingCommands.push(() => {
-        cmd.parameters.command_id = this.listener.curCmdId;
-        this.mm.broadcastAsyncMessage(
-            "Marionette:pollForReadyState" + this.curBrowser.curFrameId,
-            cmd.parameters);
-      });
-      yield this.listener.get({url: url, pageTimeout: this.pageTimeout});
-      break;
-
-    case Context.CHROME:
-      // At least on desktop, navigating in chrome scope does not
-      // correspond to something a user can do, and leaves marionette
-      // and the browser in an unusable state. Return a generic error insted.
-      // TODO: Error codes need to be refined as a part of bug 1100545 and
-      // bug 945729.
-      if (this.appName == "Firefox") {
-        throw new UnknownError("Cannot navigate in chrome context");
-      }
-
-      this.getCurrentWindow().location.href = url;
-      yield this.pageLoadPromise();
-      break;
-  }
-};
-
-GeckoDriver.prototype.pageLoadPromise = function() {
-  let win = this.getCurrentWindow();
-  let timeout = this.pageTimeout;
-  let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-  let start = new Date().getTime();
-  let end = null;
-
-  return new Promise((resolve) => {
-    let checkLoad = function() {
-      end = new Date().getTime();
-      let elapse = end - start;
-      if (timeout == null || elapse <= timeout) {
-        if (win.document.readyState == "complete") {
-          resolve();
-        } else {
-          checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
-        }
-      } else {
-        throw new UnknownError("Error loading page");
-      }
-    };
-    checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
-  });
-};
-
-/**
- * Get a string representing the current URL.
- *
- * On Desktop this returns a string representation of the URL of the
- * current top level browsing context.  This is equivalent to
- * document.location.href.
- *
- * When in the context of the chrome, this returns the canonical URL
- * of the current resource.
- */
-GeckoDriver.prototype.getCurrentUrl = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      resp.value = this.getCurrentWindow().location.href;
-      break;
-
-    case Context.CONTENT:
-      let isB2G = this.appName == "B2G";
-      resp.value = yield this.listener.getCurrentUrl({isB2G: isB2G});
-      break;
-  }
-};
-
-/** Gets the current title of the window. */
-GeckoDriver.prototype.getTitle = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      resp.value = win.document.documentElement.getAttribute("title");
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.getTitle();
-      break;
-  }
-};
-
-/** Gets the current type of the window. */
-GeckoDriver.prototype.getWindowType = function(cmd, resp) {
-  let win = this.getCurrentWindow();
-  resp.value = win.document.documentElement.getAttribute("windowtype");
-};
-
-/** Gets the page source of the content document. */
-GeckoDriver.prototype.getPageSource = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let s = new win.XMLSerializer();
-      resp.value = s.serializeToString(win.document);
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.getPageSource();
-      break;
-  }
-};
-
-/** Go back in history. */
-GeckoDriver.prototype.goBack = function(cmd, resp) {
-  yield this.listener.goBack();
-};
-
-/** Go forward in history. */
-GeckoDriver.prototype.goForward = function(cmd, resp) {
-  yield this.listener.goForward();
-};
-
-/** Refresh the page. */
-GeckoDriver.prototype.refresh = function(cmd, resp) {
-  yield this.listener.refresh();
-};
-
-/**
- * Get the current window's handle. On desktop this typically corresponds
- * to the currently selected tab.
- *
- * Return an opaque server-assigned identifier to this window that
- * uniquely identifies it within this Marionette instance.  This can
- * be used to switch to this window at a later point.
- *
- * @return {string}
- *     Unique window handle.
- */
-GeckoDriver.prototype.getWindowHandle = function(cmd, resp) {
-  // curFrameId always holds the current tab.
-  if (this.curBrowser.curFrameId && this.appName != "B2G") {
-    resp.value = this.curBrowser.curFrameId;
-    return;
-  }
-
-  for (let i in this.browsers) {
-    if (this.curBrowser == this.browsers[i]) {
-      resp.value = i;
-      return;
-    }
-  }
-};
-
-/**
- * Forces an update for the given browser's id.
- */
-GeckoDriver.prototype.updateIdForBrowser = function (browser, newId) {
-  this._browserIds.set(browser.permanentKey, newId);
-};
-
-/**
- * Retrieves a listener id for the given xul browser element. In case
- * the browser is not known, an attempt is made to retrieve the id from
- * a CPOW, and null is returned if this fails.
- */
-GeckoDriver.prototype.getIdForBrowser = function getIdForBrowser(browser) {
-  if (browser === null) {
-    return null;
-  }
-  let permKey = browser.permanentKey;
-  if (this._browserIds.has(permKey)) {
-    return this._browserIds.get(permKey);
-  }
-
-  let winId = browser.outerWindowID;
-  if (winId) {
-    winId += "";
-    this._browserIds.set(permKey, winId);
-    return winId;
-  }
-  return null;
-},
-
-/**
- * Get a list of top-level browsing contexts.  On desktop this typically
- * corresponds to the set of open tabs.
- *
- * Each window handle is assigned by the server and is guaranteed unique,
- * however the return array does not have a specified ordering.
- *
- * @return {Array.<string>}
- *     Unique window handles.
- */
-GeckoDriver.prototype.getWindowHandles = function(cmd, resp) {
-  let rv = [];
-  let winEn = this.getWinEnumerator();
-  while (winEn.hasMoreElements()) {
-    let win = winEn.getNext();
-    if (win.gBrowser && this.appName != "B2G") {
-      let tabbrowser = win.gBrowser;
-      for (let i = 0; i < tabbrowser.browsers.length; ++i) {
-        let winId = this.getIdForBrowser(tabbrowser.getBrowserAtIndex(i));
-        if (winId !== null) {
-          rv.push(winId);
-        }
-      }
-    } else {
-      // XUL Windows, at least, do not have gBrowser
-      let winId = win.QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIDOMWindowUtils)
-          .outerWindowID;
-      winId += (this.appName == "B2G") ? "-b2g" : "";
-      rv.push(winId);
-    }
-  }
-  resp.value = rv;
-};
-
-/**
- * Get the current window's handle.  This corresponds to a window that
- * may itself contain tabs.
- *
- * Return an opaque server-assigned identifier to this window that
- * uniquely identifies it within this Marionette instance.  This can
- * be used to switch to this window at a later point.
- *
- * @return {string}
- *     Unique window handle.
- */
-GeckoDriver.prototype.getChromeWindowHandle = function(cmd, resp) {
-  for (let i in this.browsers) {
-    if (this.curBrowser == this.browsers[i]) {
-      resp.value = i;
-      return;
-    }
-  }
-};
-
-/**
- * Returns identifiers for each open chrome window for tests interested in
- * managing a set of chrome windows and tabs separately.
- *
- * @return {Array.<string>}
- *     Unique window handles.
- */
-GeckoDriver.prototype.getChromeWindowHandles = function(cmd, resp) {
-  let rv = [];
-  let winEn = this.getWinEnumerator();
-  while (winEn.hasMoreElements()) {
-    let foundWin = winEn.getNext();
-    let winId = foundWin.QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIDOMWindowUtils)
-        .outerWindowID;
-    winId = winId + ((this.appName == "B2G") ? "-b2g" : "");
-    rv.push(winId);
-  }
-  resp.value = rv;
-};
-
-/**
- * Get the current window position.
- *
- * @return {Object.<string, number>}
- *     Object with x and y coordinates.
- */
-GeckoDriver.prototype.getWindowPosition = function(cmd, resp) {
-  let win = this.getCurrentWindow();
-  resp.value = {x: win.screenX, y: win.screenY};
-};
-
-/**
- * Set the window position of the browser on the OS Window Manager
- *
- * @param {number} x
- *     X coordinate of the top/left of the window that it will be
- *     moved to.
- * @param {number} y
- *     Y coordinate of the top/left of the window that it will be
- *     moved to.
- */
-GeckoDriver.prototype.setWindowPosition = function(cmd, resp) {
-  if (this.appName != "Firefox") {
-    throw new WebDriverError("Unable to set the window position on mobile");
-  }
-
-  let x = parseInt(cmd.parameters.x);
-  let y  = parseInt(cmd.parameters.y);
-  if (isNaN(x) || isNaN(y)) {
-    throw new UnknownError("x and y arguments should be integers");
-  }
-
-  let win = this.getCurrentWindow();
-  win.moveTo(x, y);
-};
-
-/**
- * Switch current top-level browsing context by name or server-assigned ID.
- * Searches for windows by name, then ID.  Content windows take precedence.
- *
- * @param {string} name
- *     Target name or ID of the window to switch to.
- */
-GeckoDriver.prototype.switchToWindow = function(cmd, resp) {
-  let switchTo = cmd.parameters.name;
-  let isB2G = this.appName == "B2G";
-  let found;
-
-  let getOuterWindowId = function(win) {
-    let rv = win.QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIDOMWindowUtils)
-        .outerWindowID;
-    rv += isB2G ? "-b2g" : "";
-    return rv;
-  };
-
-  let byNameOrId = function(name, outerId, contentWindowId) {
-    return switchTo == name ||
-        switchTo == contentWindowId ||
-        switchTo == outerId;
-  };
-
-  let winEn = this.getWinEnumerator();
-  while (winEn.hasMoreElements()) {
-    let win = winEn.getNext();
-    let outerId = getOuterWindowId(win);
-
-    if (win.gBrowser && !isB2G) {
-      let tabbrowser = win.gBrowser;
-      for (let i = 0; i < tabbrowser.browsers.length; ++i) {
-        let browser = tabbrowser.getBrowserAtIndex(i);
-        let contentWindowId = this.getIdForBrowser(browser);
-        if (byNameOrId(win.name, contentWindowId, outerId)) {
-          found = {
-            win: win,
-            outerId: outerId,
-            tabIndex: i,
-            contentId: contentWindowId
-          };
-          break;
-        }
-      }
-    } else {
-      if (byNameOrId(win.name, outerId)) {
-        found = {win: win, outerId: outerId};
-        break;
-      }
-    }
-  }
-
-  if (found) {
-    // As in content, switching to a new window invalidates a sandbox
-    // for reuse.
-    this.sandbox = null;
-
-    // Initialise Marionette if browser has not been seen before,
-    // otherwise switch to known browser and activate the tab if it's a
-    // content browser.
-    if (!(found.outerId in this.browsers)) {
-      let registerBrowsers, browserListening;
-      if (found.contentId) {
-        registerBrowsers = this.registerPromise();
-        browserListening = this.listeningPromise();
-      }
-
-      this.startBrowser(found.win, false /* isNewSession */);
-
-      if (registerBrowsers && browserListening) {
-        yield registerBrowsers;
-        yield browserListening;
-      }
-    } else {
-      utils.window = found.win;
-      this.curBrowser = this.browsers[found.outerId];
-
-      if (found.contentId) {
-        this.curBrowser.switchToTab(found.tabIndex);
-      }
-    }
-  } else {
-    throw new NoSuchWindowError(`Unable to locate window: ${switchTo}`);
-  }
-};
-
-GeckoDriver.prototype.getActiveFrame = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      // no frame means top-level
-      resp.value = null;
-      if (this.curFrame) {
-        resp.value = this.curBrowser.elementManager
-            .addToKnownElements(this.curFrame.frameElement);
-      }
-      break;
-
-    case Context.CONTENT:
-      resp.value = this.currentFrameElement;
-      break;
-  }
-};
-
-/**
- * Switch to a given frame within the current window.
- *
- * @param {Object} element
- *     A web element reference to the element to switch to.
- * @param {(string|number)} id
- *     If element is not defined, then this holds either the id, name,
- *     or index of the frame to switch to.
- */
-GeckoDriver.prototype.switchToFrame = function(cmd, resp) {
-  let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-  let curWindow = this.getCurrentWindow();
-
-  let checkLoad = function() {
-    let errorRegex = /about:.+(error)|(blocked)\?/;
-    let curWindow = this.getCurrentWindow();
-    if (curWindow.document.readyState == "complete") {
-      return;
-    } else if (curWindow.document.readyState == "interactive" &&
-        errorRegex.exec(curWindow.document.baseURI)) {
-      throw new UnknownError("Error loading page");
-    }
-
-    checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
-  };
-
-  if (this.context == Context.CHROME) {
-    let foundFrame = null;
-    if ((cmd.parameters.id == null) && (cmd.parameters.element == null)) {
-      this.curFrame = null;
-      if (cmd.parameters.focus) {
-        this.mainFrame.focus();
-      }
-      checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
-      return;
-    }
-    if (cmd.parameters.element != undefined) {
-      if (this.curBrowser.elementManager.seenItems[cmd.parameters.element]) {
-        // HTMLIFrameElement
-        let wantedFrame = this.curBrowser.elementManager
-            .getKnownElement(cmd.parameters.element, curWindow);
-        // Deal with an embedded xul:browser case
-        if (wantedFrame.tagName == "xul:browser" || wantedFrame.tagName == "browser") {
-          curWindow = wantedFrame.contentWindow;
-          this.curFrame = curWindow;
-          if (cmd.parameters.focus) {
-            this.curFrame.focus();
-          }
-          checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
-          return;
-        }
-        // else, assume iframe
-        let frames = curWindow.document.getElementsByTagName("iframe");
-        let numFrames = frames.length;
-        for (let i = 0; i < numFrames; i++) {
-          if (XPCNativeWrapper(frames[i]) == XPCNativeWrapper(wantedFrame)) {
-            curWindow = frames[i].contentWindow;
-            this.curFrame = curWindow;
-            if (cmd.parameters.focus) {
-              this.curFrame.focus();
-            }
-            checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
-            return;
-        }
-      }
-    }
-  }
-  switch(typeof(cmd.parameters.id)) {
-    case "string" :
-      let foundById = null;
-      let frames = curWindow.document.getElementsByTagName("iframe");
-      let numFrames = frames.length;
-      for (let i = 0; i < numFrames; i++) {
-        //give precedence to name
-        let frame = frames[i];
-        if (frame.getAttribute("name") == cmd.parameters.id) {
-          foundFrame = i;
-          curWindow = frame.contentWindow;
-          break;
-        } else if ((foundById == null) && (frame.id == cmd.parameters.id)) {
-          foundById = i;
-        }
-      }
-      if ((foundFrame == null) && (foundById != null)) {
-        foundFrame = foundById;
-        curWindow = frames[foundById].contentWindow;
-      }
-      break;
-    case "number":
-      if (curWindow.frames[cmd.parameters.id] != undefined) {
-        foundFrame = cmd.parameters.id;
-        curWindow = curWindow.frames[foundFrame].frameElement.contentWindow;
-      }
-      break;
-    }
-    if (foundFrame != null) {
-      this.curFrame = curWindow;
-      if (cmd.parameters.focus) {
-        this.curFrame.focus();
-      }
-      checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
-    } else {
-      throw new NoSuchFrameError(
-          `Unable to locate frame: ${cmd.parameters.id}`);
-    }
-  }
-  else {
-    if ((!cmd.parameters.id) && (!cmd.parameters.element) &&
-        (this.curBrowser.frameManager.currentRemoteFrame !== null)) {
-      // We're currently using a ChromeMessageSender for a remote frame, so this
-      // request indicates we need to switch back to the top-level (parent) frame.
-      // We'll first switch to the parent's (global) ChromeMessageBroadcaster, so
-      // we send the message to the right listener.
-      this.switchToGlobalMessageManager();
-    }
-    cmd.command_id = cmd.id;
-
-    let res = yield this.listener.switchToFrame(cmd.parameters);
-    if (res) {
-      let {win: winId, frame: frameId} = res;
-      this.mm = this.curBrowser.frameManager.getFrameMM(winId, frameId);
-
-      let registerBrowsers = this.registerPromise();
-      let browserListening = this.listeningPromise();
-
-      this.oopFrameId =
-          this.curBrowser.frameManager.switchToFrame(winId, frameId);
-
-      yield registerBrowsers;
-      yield browserListening;
-    }
-  }
-};
-
-/**
- * Set timeout for searching for elements.
- *
- * @param {number} ms
- *     Search timeout in milliseconds.
- */
-GeckoDriver.prototype.setSearchTimeout = function(cmd, resp) {
-  let ms = parseInt(cmd.parameters.ms);
-  if (isNaN(ms)) {
-    throw new WebDriverError("Not a Number");
-  }
-  this.searchTimeout = ms;
-};
-
-/**
- * Set timeout for page loading, searching, and scripts.
- *
- * @param {string} type
- *     Type of timeout.
- * @param {number} ms
- *     Timeout in milliseconds.
- */
-GeckoDriver.prototype.timeouts = function(cmd, resp) {
-  let typ = cmd.parameters.type;
-  let ms = parseInt(cmd.parameters.ms);
-  if (isNaN(ms)) {
-    throw new WebDriverError("Not a Number");
-  }
-
-  switch (typ) {
-    case "implicit":
-      this.setSearchTimeout(cmd, resp);
-      break;
-
-    case "script":
-      this.setScriptTimeout(cmd, resp);
-      break;
-
-    default:
-      this.pageTimeout = ms;
-      break;
-  }
-};
-
-/** Single tap. */
-GeckoDriver.prototype.singleTap = function(cmd, resp) {
-  let {id, x, y} = cmd.parameters;
-
-  switch (this.context) {
-    case Context.CHROME:
-      throw new WebDriverError("Command 'singleTap' is not available in chrome context");
-
-    case Context.CONTENT:
-      this.addFrameCloseListener("tap");
-      yield this.listener.singleTap({id: id, corx: x, cory: y});
-      break;
-  }
-};
-
-/**
- * An action chain.
- *
- * @param {Object} value
- *     A nested array where the inner array represents each event,
- *     and the outer array represents a collection of events.
- *
- * @return {number}
- *     Last touch ID.
- */
-GeckoDriver.prototype.actionChain = function(cmd, resp) {
-  let {chain, nextId} = cmd.parameters;
-
-  switch (this.context) {
-    case Context.CHROME:
-      if (this.appName != "Firefox") {
-        // be conservative until this has a use case and is established
-        // to work as expected on b2g/fennec
-        throw new WebDriverError(
-            "Command 'actionChain' is not available in chrome context");
-      }
-
-      let cbs = {};
-      cbs.onSuccess = val => resp.value = val;
-      cbs.onError = err => { throw err };
-
-      let win = this.getCurrentWindow();
-      let elm = this.curBrowser.elementManager;
-      this.actions.dispatchActions(chain, nextId, win, elm, cbs);
-      break;
-
-    case Context.CONTENT:
-      this.addFrameCloseListener("action chain");
-      resp.value = yield this.listener.actionChain({chain: chain, nextId: nextId});
-      break;
-  }
-};
-
-/**
- * A multi-action chain.
- *
- * @param {Object} value
- *     A nested array where the inner array represents eache vent,
- *     the middle array represents a collection of events for each
- *     finger, and the outer array represents all fingers.
- */
-GeckoDriver.prototype.multiAction = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      throw new WebDriverError("Command 'multiAction' is not available in chrome context");
-
-    case Context.CONTENT:
-      this.addFrameCloseListener("multi action chain");
-      yield this.listener.multiAction(
-          {value: value, maxlen: max_len} = cmd.parameters);
-      break;
-  }
-};
-
-/**
- * Find an element using the indicated search strategy.
- *
- * @param {string} using
- *     Indicates which search method to use.
- * @param {string} value
- *     Value the client is looking for.
- */
-GeckoDriver.prototype.findElement = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      resp.value = yield new Promise((resolve, reject) => {
-        let win = this.getCurrentWindow();
-        this.curBrowser.elementManager.find(
-            win,
-            cmd.parameters,
-            this.searchTimeout,
-            false /* all */,
-            resolve,
-            reject);
-      }).then(null, e => { throw e; });
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.findElementContent({
-        value: cmd.parameters.value,
-        using: cmd.parameters.using,
-        element: cmd.parameters.element,
-        searchTimeout: this.searchTimeout});
-      break;
-  }
-};
-
-/**
- * Find element using the indicated search strategy starting from a
- * known element.  Used for WebDriver Compatibility only.
- *
- * @param {string} using
- *     Indicates which search method to use.
- * @param {string} value
- *     Value the client is looking for.
- * @param {string} id
- *     Value of the element to start from.
- */
-GeckoDriver.prototype.findChildElement = function(cmd, resp) {
-  resp.value = yield this.listener.findElementContent({
-    value: cmd.parameters.value,
-    using: cmd.parameters.using,
-    element: cmd.parameters.id,
-    searchTimeout: this.searchTimeout});
-};
-
-/**
- * Find elements using the indicated search strategy.
- *
- * @param {string} using
- *     Indicates which search method to use.
- * @param {string} value
- *     Value the client is looking for.
- */
-GeckoDriver.prototype.findElements = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      resp.value = yield new Promise((resolve, reject) => {
-        let win = this.getCurrentWindow();
-        this.curBrowser.elementManager.find(
-            win,
-            cmd.parameters,
-            this.searchTimeout,
-            true /* all */,
-            resolve,
-            reject);
-      }).then(null, e => { throw new NoSuchElementError(e.message); });
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.findElementsContent({
-        value: cmd.parameters.value,
-        using: cmd.parameters.using,
-        element: cmd.parameters.element,
-        searchTimeout: this.searchTimeout});
-      break;
-  }
-};
-
-/**
- * Find elements using the indicated search strategy starting from a
- * known element.  Used for WebDriver Compatibility only.
- *
- * @param {string} using
- *     Indicates which search method to use.
- * @param {string} value
- *     Value the client is looking for.
- * @param {string} id
- *     Value of the element to start from.
- */
-GeckoDriver.prototype.findChildElements = function(cmd, resp) {
-  resp.value = yield this.listener.findElementsContent({
-    value: cmd.parameters.value,
-    using: cmd.parameters.using,
-    element: cmd.parameters.id,
-    searchTimeout: this.searchTimeout});
-};
-
-/** Return the active element on the page. */
-GeckoDriver.prototype.getActiveElement = function(cmd, resp) {
-  resp.value = yield this.listener.getActiveElement();
-};
-
-/**
- * Send click event to element.
- *
- * @param {string} id
- *     Reference ID to the element that will be clicked.
- */
-GeckoDriver.prototype.clickElement = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      // click atom fails, fall back to click() action
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      el.click();
-      break;
-
-    case Context.CONTENT:
-      // We need to protect against the click causing an OOP frame to close.
-      // This fires the mozbrowserclose event when it closes so we need to
-      // listen for it and then just send an error back. The person making the
-      // call should be aware something isnt right and handle accordingly
-      this.addFrameCloseListener("click");
-      yield this.listener.clickElement({id: id});
-      break;
-  }
-};
-
-/**
- * Get a given attribute of an element.
- *
- * @param {string} id
- *     Reference ID to the element that will be inspected.
- * @param {string} name
- *     Name of the attribute to retrieve.
- */
-GeckoDriver.prototype.getElementAttribute = function(cmd, resp) {
-  let {id, name} = cmd.parameters;
-
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      resp.value = utils.getElementAttribute(el, name);
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.getElementAttribute({id: id, name: name});
-      break;
-  }
-};
-
-/**
- * Get the text of an element, if any.  Includes the text of all child
- * elements.
- *
- * @param {string} id
- *     Reference ID to the element that will be inspected.
- */
-GeckoDriver.prototype.getElementText = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      // for chrome, we look at text nodes, and any node with a "label" field
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      let lines = [];
-      this.getVisibleText(el, lines);
-      resp.value = lines.join("\n");
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.getElementText({id: id});
-      break;
-  }
-};
-
-/**
- * Get the tag name of the element.
- *
- * @param {string} id
- *     Reference ID to the element that will be inspected.
- */
-GeckoDriver.prototype.getElementTagName = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      resp.value = el.tagName.toLowerCase();
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.getElementTagName({id: id});
-      break;
-  }
-};
-
-/**
- * Check if element is displayed.
- *
- * @param {string} id
- *     Reference ID to the element that will be inspected.
- */
-GeckoDriver.prototype.isElementDisplayed = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      resp.value = utils.isElementDisplayed(el);
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.isElementDisplayed({id: id});
-      break;
-  }
-};
-
-/**
- * Return the property of the computed style of an element.
- *
- * @param {string} id
- *     Reference ID to the element that will be checked.
- * @param {string} propertyName
- *     CSS rule that is being requested.
- */
-GeckoDriver.prototype.getElementValueOfCssProperty = function(cmd, resp) {
-  let {id, propertyName: prop} = cmd.parameters;
-
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      let sty = win.document.defaultView.getComputedStyle(el, null);
-      resp.value = sty.getPropertyValue(prop);
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.getElementValueOfCssProperty(
-          {id: id, propertyName: prop});
-      break;
-  }
-};
-
-/**
- * Submit a form on a content page by either using form or element in
- * a form.
- *
- * @param {string} id
- *     Reference to the elemen that will be checked.
- */
-GeckoDriver.prototype.submitElement = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      throw new WebDriverError(
-          "Command 'submitElement' is not available in chrome context");
-
-    case Context.CONTENT:
-      yield this.listener.submitElement({id: cmd.parameters.id});
-      break;
-  }
-};
-
-/**
- * Check if element is enabled.
- *
- * @param {string} id
- *     Reference ID to the element that will be checked.
- */
-GeckoDriver.prototype.isElementEnabled = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      // Selenium atom doesn't quite work here
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      resp.value = !(!!el.disabled);
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.isElementEnabled({id: id});
-      break;
-  }
-},
-
-/**
- * Check if element is selected.
- *
- * @param {string} id
- *     Reference ID to the element that will be checked.
- */
-GeckoDriver.prototype.isElementSelected = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      // Selenium atom doesn't quite work here
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      if (typeof el.checked != "undefined") {
-        resp.value = !!el.checked;
-      } else if (typeof el.selected != "undefined") {
-        resp.value = !!el.selected;
-      } else {
-        resp.value = true;
-      }
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.isElementSelected({id: id});
-      break;
-  }
-};
-
-GeckoDriver.prototype.getElementSize = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      let rect = el.getBoundingClientRect();
-      resp.value = {width: rect.width, height: rect.height};
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.getElementSize({id: id});
-      break;
-  }
-};
-
-GeckoDriver.prototype.getElementRect = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      let rect = el.getBoundingClientRect();
-      resp.value = {
-        x: rect.x + win.pageXOffset,
-        y: rect.y + win.pageYOffset,
-        width: rect.width,
-        height: rect.height
-      };
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.getElementRect({id: id});
-      break;
-  }
-};
-
-/**
- * Send key presses to element after focusing on it.
- *
- * @param {string} id
- *     Reference ID to the element that will be checked.
- * @param {string} value
- *     Value to send to the element.
- */
-GeckoDriver.prototype.sendKeysToElement = function(cmd, resp) {
-  let {id, value} = cmd.parameters;
-
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      utils.sendKeysToElement(
-          win,
-          el,
-          value,
-          () => {},
-          e => { throw e; },
-          cmd.id,
-          true /* ignore visibility check */);
-      break;
-
-    case Context.CONTENT:
-      yield this.listener.sendKeysToElement({id: id, value: value});
-      break;
-  }
-};
-
-/** Sets the test name.  The test name is used for logging purposes. */
-GeckoDriver.prototype.setTestName = function(cmd, resp) {
-  let val = cmd.parameters.value;
-  this.testName = val;
-  yield this.listener.setTestName({value: val});
-};
-
-/**
- * Clear the text of an element.
- *
- * @param {string} id
- *     Reference ID to the element that will be cleared.
- */
-GeckoDriver.prototype.clearElement = function(cmd, resp) {
-  let id = cmd.parameters.id;
-
-  switch (this.context) {
-    case Context.CHROME:
-      // the selenium atom doesn't work here
-      let win = this.getCurrentWindow();
-      let el = this.curBrowser.elementManager.getKnownElement(id, win);
-      if (el.nodeName == "textbox") {
-        el.value = "";
-      } else if (el.nodeName == "checkbox") {
-        el.checked = false;
-      }
-      break;
-
-    case Context.CONTENT:
-      yield this.listener.clearElement({id: id});
-      break;
-  }
-};
-
-/**
- * Get an element's location on the page.
- *
- * The returned point will contain the x and y coordinates of the
- * top left-hand corner of the given element.  The point (0,0)
- * refers to the upper-left corner of the document.
- *
- * @return {Object.<string, number>}
- *     A point containing X and Y coordinates as properties.
- */
-GeckoDriver.prototype.getElementLocation = function(cmd, resp) {
-  resp.value = yield this.listener.getElementLocation(
-      {id: cmd.parameters.id});
-};
-
-/** Add a cookie to the document. */
-GeckoDriver.prototype.addCookie = function(cmd, resp) {
-  yield this.listener.addCookie({cookie: cmd.parameters.cookie});
-};
-
-/**
- * Get all the cookies for the current domain.
- *
- * This is the equivalent of calling {@code document.cookie} and parsing
- * the result.
- */
-GeckoDriver.prototype.getCookies = function(cmd, resp) {
-  resp.value = yield this.listener.getCookies();
-};
-
-/** Delete all cookies that are visible to a document. */
-GeckoDriver.prototype.deleteAllCookies = function(cmd, resp) {
-  yield this.listener.deleteAllCookies();
-};
-
-/** Delete a cookie by name. */
-GeckoDriver.prototype.deleteCookie = function(cmd, resp) {
-  yield this.listener.deleteCookie({name: cmd.parameters.name});
-};
-
-/**
- * Close the current window, ending the session if it's the last
- * window currently open.
- *
- * On B2G this method is a noop and will return immediately.
- */
-GeckoDriver.prototype.close = function(cmd, resp) {
-  // can't close windows on B2G
-  if (this.appName == "B2G") {
-    return;
-  }
-
-  let nwins = 0;
-  let winEn = this.getWinEnumerator();
-  while (winEn.hasMoreElements()) {
-    let win = winEn.getNext();
-
-    // count both windows and tabs
-    if (win.gBrowser) {
-      nwins += win.gBrowser.browsers.length;
-    } else {
-      nwins++;
-    }
-  }
-
-  // if there is only 1 window left, delete the session
-  if (nwins == 1) {
-    this.sessionTearDown();
-    return;
-  }
-
-  try {
-    if (this.mm != globalMessageManager) {
-      this.mm.removeDelayedFrameScript(FRAME_SCRIPT);
-    }
-
-    if (this.curBrowser.tab) {
-      this.curBrowser.closeTab();
-    } else {
-      this.getCurrentWindow().close();
-    }
-  } catch (e) {
-    throw new UnknownError(`Could not close window: ${e.message}`);
-  }
-};
-
-/**
- * Close the currently selected chrome window, ending the session if it's the last
- * window currently open.
- *
- * On B2G this method is a noop and will return immediately.
- */
-GeckoDriver.prototype.closeChromeWindow = function(cmd, resp) {
-  // can't close windows on B2G
-  if (this.appName == "B2G") {
-    return;
-  }
-
-  // Get the total number of windows
-  let nwins = 0;
-  let winEn = this.getWinEnumerator();
-  while (winEn.hasMoreElements()) {
-    nwins++;
-    winEn.getNext();
-  }
-
-  // if there is only 1 window left, delete the session
-  if (nwins == 1) {
-    this.sessionTearDown();
-    return;
-  }
-
-  try {
-    this.mm.removeDelayedFrameScript(FRAME_SCRIPT);
-    this.getCurrentWindow().close();
-  } catch (e) {
-    throw new UnknownError(`Could not close window: ${e.message}`);
-  }
-};
-
-/**
- * Deletes the session.
- *
- * If it is a desktop environment, it will close all listeners.
- *
- * If it is a B2G environment, it will make the main content listener
- * sleep, and close all other listeners.  The main content listener
- * persists after disconnect (it's the homescreen), and can safely
- * be reused.
- */
-GeckoDriver.prototype.sessionTearDown = function(cmd, resp) {
-  if (this.curBrowser != null) {
-    if (this.appName == "B2G") {
-      globalMessageManager.broadcastAsyncMessage(
-          "Marionette:sleepSession" + this.curBrowser.mainContentId, {});
-      this.curBrowser.knownFrames.splice(
-          this.curBrowser.knownFrames.indexOf(this.curBrowser.mainContentId), 1);
-    } else {
-      // don't set this pref for B2G since the framescript can be safely reused
-      Services.prefs.setBoolPref("marionette.contentListener", false);
-    }
-
-    // delete session in each frame in each browser
-    for (let win in this.browsers) {
-      let browser = this.browsers[win];
-      for (let i in browser.knownFrames) {
-        globalMessageManager.broadcastAsyncMessage(
-            "Marionette:deleteSession" + browser.knownFrames[i], {});
-      }
-    }
-
-    let winEn = this.getWinEnumerator();
-    while (winEn.hasMoreElements()) {
-      winEn.getNext().messageManager.removeDelayedFrameScript(FRAME_SCRIPT);
-    }
-
-    this.curBrowser.frameManager.removeSpecialPowers();
-    this.curBrowser.frameManager.removeMessageManagerListeners(
-        globalMessageManager);
-  }
-
-  this.switchToGlobalMessageManager();
-
-  // reset frame to the top-most frame
-  this.curFrame = null;
-  if (this.mainFrame) {
-    this.mainFrame.focus();
-  }
-
-  this.sessionId = null;
-  this.deleteFile("marionetteChromeScripts");
-  this.deleteFile("marionetteContentScripts");
-
-  if (this.observing !== null) {
-    for (let topic in this.observing) {
-      Services.obs.removeObserver(this.observing[topic], topic);
-    }
-    this.observing = null;
-  }
-};
-
-/**
- * Processes the "deleteSession" request from the client by tearing down
- * the session and responding "ok".
- */
-GeckoDriver.prototype.deleteSession = function(cmd, resp) {
-  this.sessionTearDown();
-};
-
-/** Returns the current status of the Application Cache. */
-GeckoDriver.prototype.getAppCacheStatus = function(cmd, resp) {
-  resp.value = yield this.listener.getAppCacheStatus();
-};
-
-GeckoDriver.prototype.importScript = function(cmd, resp) {
-  let script = cmd.parameters.script;
-
-  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-      .createInstance(Ci.nsIScriptableUnicodeConverter);
-  converter.charset = "UTF-8";
-  let result = {};
-  let data = converter.convertToByteArray(cmd.parameters.script, result);
-  let ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
-  ch.init(ch.MD5);
-  ch.update(data, data.length);
-  let hash = ch.finish(true);
-  // return if we've already imported this script
-  if (this.importedScriptHashes[this.context].indexOf(hash) > -1) {
-    return;
-  }
-  this.importedScriptHashes[this.context].push(hash);
-
-  switch (this.context) {
-    case Context.CHROME:
-      let file;
-      if (this.importedScripts.exists()) {
-        file = FileUtils.openFileOutputStream(this.importedScripts,
-            FileUtils.MODE_APPEND | FileUtils.MODE_WRONLY);
-      } else {
-        // the permission bits here don't actually get set (bug 804563)
-        this.importedScripts.createUnique(
-            Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0666", 8));
-        file = FileUtils.openFileOutputStream(this.importedScripts,
-            FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
-        this.importedScripts.permissions = parseInt("0666", 8);
-      }
-      file.write(script, script.length);
-      file.close();
-      break;
-
-    case Context.CONTENT:
-      yield this.listener.importScript({script: script});
-      break;
-  }
-};
-
-GeckoDriver.prototype.clearImportedScripts = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      this.deleteFile("marionetteChromeScripts");
-      break;
-
-    case Context.CONTENT:
-      this.deleteFile("marionetteContentScripts");
-      break;
-  }
-};
-
-/**
- * Takes a screenshot of a web element, current frame, or viewport.
- *
- * The screen capture is returned as a lossless PNG image encoded as
- * a base 64 string.
- *
- * If called in the content context, the <code>id</code> argument is not null
- * and refers to a present and visible web element's ID, the capture area
- * will be limited to the bounding box of that element. Otherwise, the
- * capture area will be the bounding box of the current frame.
- *
- * If called in the chrome context, the screenshot will always represent the
- * entire viewport.
- *
- * @param {string} id
- *     Reference to a web element.
- * @param {string} highlights
- *     List of web elements to highlight.
- *
- * @return {string}
- *     PNG image encoded as base64 encoded string.
- */
-GeckoDriver.prototype.takeScreenshot = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      let win = this.getCurrentWindow();
-      let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-      let doc;
-      if (this.appName == "B2G") {
-        doc = win.document.body;
-      } else {
-        doc = win.document.getElementsByTagName("window")[0];
-      }
-      let docRect = doc.getBoundingClientRect();
-      let width = docRect.width;
-      let height = docRect.height;
-
-      // Convert width and height from CSS pixels (potentially fractional)
-      // to device pixels (integer).
-      let scale = win.devicePixelRatio;
-      canvas.setAttribute("width", Math.round(width * scale));
-      canvas.setAttribute("height", Math.round(height * scale));
-
-      let context = canvas.getContext("2d");
-      let flags;
-      if (this.appName == "B2G") {
-        flags =
-          context.DRAWWINDOW_DRAW_CARET |
-          context.DRAWWINDOW_DRAW_VIEW |
-          context.DRAWWINDOW_USE_WIDGET_LAYERS;
-      } else {
-        // Bug 1075168: CanvasRenderingContext2D image is distorted
-        // when using certain flags in chrome context.
-        flags =
-          context.DRAWWINDOW_DRAW_VIEW |
-          context.DRAWWINDOW_USE_WIDGET_LAYERS;
-      }
-      context.scale(scale, scale);
-      context.drawWindow(win, 0, 0, width, height, "rgb(255,255,255)", flags);
-      let dataUrl = canvas.toDataURL("image/png", "");
-      let data = dataUrl.substring(dataUrl.indexOf(",") + 1);
-      resp.value = data;
-      break;
-
-    case Context.CONTENT:
-      resp.value = yield this.listener.takeScreenshot({
-        id: cmd.parameters.id,
-        highlights: cmd.parameters.highlights,
-        full: cmd.parameters.full});
-      break;
-  }
-};
-
-/**
- * Get the current browser orientation.
- *
- * Will return one of the valid primary orientation values
- * portrait-primary, landscape-primary, portrait-secondary, or
- * landscape-secondary.
- */
-GeckoDriver.prototype.getScreenOrientation = function(cmd, resp) {
-  resp.value = this.getCurrentWindow().screen.mozOrientation;
-};
-
-/**
- * Set the current browser orientation.
- *
- * The supplied orientation should be given as one of the valid
- * orientation values.  If the orientation is unknown, an error will
- * be raised.
- *
- * Valid orientations are "portrait" and "landscape", which fall
- * back to "portrait-primary" and "landscape-primary" respectively,
- * and "portrait-secondary" as well as "landscape-secondary".
- */
-GeckoDriver.prototype.setScreenOrientation = function(cmd, resp) {
-  const ors = [
-    "portrait", "landscape",
-    "portrait-primary", "landscape-primary",
-    "portrait-secondary", "landscape-secondary"
-  ];
-
-  let or = String(cmd.parameters.orientation);
-  let mozOr = or.toLowerCase();
-  if (ors.indexOf(mozOr) < 0) {
-    throw new WebDriverError(`Unknown screen orientation: ${or}`);
-  }
-
-  let win = this.getCurrentWindow();
-  if (!win.screen.mozLockOrientation(mozOr)) {
-    throw new WebDriverError(`Unable to set screen orientation: ${or}`);
-  }
-};
-
-/**
- * Get the size of the browser window currently in focus.
- *
- * Will return the current browser window size in pixels. Refers to
- * window outerWidth and outerHeight values, which include scroll bars,
- * title bars, etc.
- */
-GeckoDriver.prototype.getWindowSize = function(cmd, resp) {
-  let win = this.getCurrentWindow();
-  resp.value = {width: win.outerWidth, height: win.outerHeight};
-};
-
-/**
- * Set the size of the browser window currently in focus.
- *
- * Not supported on B2G. The supplied width and height values refer to
- * the window outerWidth and outerHeight values, which include scroll
- * bars, title bars, etc.
- *
- * An error will be returned if the requested window size would result
- * in the window being in the maximized state.
- */
-GeckoDriver.prototype.setWindowSize = function(cmd, resp) {
-  if (this.appName !== "Firefox") {
-    throw new UnsupportedOperationError("Not supported on mobile");
-  }
-
-  let width = parseInt(cmd.parameters.width);
-  let height = parseInt(cmd.parameters.height);
-
-  let win = this.getCurrentWindow();
-  if (width >= win.screen.availWidth && height >= win.screen.availHeight) {
-    throw new UnsupportedOperationError("Invalid requested size, cannot maximize");
-  }
-
-  win.resizeTo(width, height);
-};
-
-/**
- * Maximizes the user agent window as if the user pressed the maximise
- * button.
- *
- * Not Supported on B2G or Fennec.
- */
-GeckoDriver.prototype.maximizeWindow = function(cmd, resp) {
-  if (this.appName != "Firefox") {
-    throw new UnsupportedOperationError("Not supported for mobile");
-  }
-
-  let win = this.getCurrentWindow();
-  win.moveTo(0,0);
-  win.resizeTo(win.screen.availWidth, win.screen.availHeight);
-};
-
-/**
- * Dismisses a currently displayed tab modal, or returns no such alert if
- * no modal is displayed.
- */
-GeckoDriver.prototype.dismissDialog = function(cmd, resp) {
-  if (!this.dialog) {
-    throw new NoAlertOpenError(
-        "No tab modal was open when attempting to dismiss the dialog");
-  }
-
-  let {button0, button1} = this.dialog.ui;
-  (button1 ? button1 : button0).click();
-  this.dialog = null;
-};
-
-/**
- * Accepts a currently displayed tab modal, or returns no such alert if
- * no modal is displayed.
- */
-GeckoDriver.prototype.acceptDialog = function(cmd, resp) {
-  if (!this.dialog) {
-    throw new NoAlertOpenError(
-        "No tab modal was open when attempting to accept the dialog");
-  }
-
-  let {button0} = this.dialog.ui;
-  button0.click();
-  this.dialog = null;
-};
-
-/**
- * Returns the message shown in a currently displayed modal, or returns a no such
- * alert error if no modal is currently displayed.
- */
-GeckoDriver.prototype.getTextFromDialog = function(cmd, resp) {
-  if (!this.dialog) {
-    throw new NoAlertOpenError(
-        "No tab modal was open when attempting to get the dialog text");
-  }
-
-  let {infoBody} = this.dialog.ui;
-  resp.value = infoBody.textContent;
-};
-
-/**
- * Sends keys to the input field of a currently displayed modal, or
- * returns a no such alert error if no modal is currently displayed. If
- * a tab modal is currently displayed but has no means for text input,
- * an element not visible error is returned.
- */
-GeckoDriver.prototype.sendKeysToDialog = function(cmd, resp) {
-  if (!this.dialog) {
-    throw new NoAlertOpenError(
-        "No tab modal was open when attempting to send keys to a dialog");
-  }
-
-  // see toolkit/components/prompts/content/commonDialog.js
-  let {loginContainer, loginTextbox} = this.dialog.ui;
-  if (loginContainer.hidden) {
-    throw new ElementNotVisibleError("This prompt does not accept text input");
-  }
-
-  let win = this.dialog.window ? this.dialog.window : this.getCurrentWindow();
-  utils.sendKeysToElement(
-      win,
-      loginTextbox,
-      cmd.parameters.value,
-      () => {},
-      e => { throw e; },
-      this.command_id,
-      true /* ignore visibility check */);
-};
-
-/**
- * Helper function to convert an outerWindowID into a UID that Marionette
- * tracks.
- */
-GeckoDriver.prototype.generateFrameId = function(id) {
-  let uid = id + (this.appName == "B2G" ? "-b2g" : "");
-  return uid;
-};
-
-/** Receives all messages from content messageManager. */
-GeckoDriver.prototype.receiveMessage = function(message) {
-  // we need to just check if we need to remove the mozbrowserclose listener
-  if (this.mozBrowserClose !== null) {
-    let win = this.getCurrentWindow();
-    win.removeEventListener("mozbrowserclose", this.mozBrowserClose, true);
-    this.mozBrowserClose = null;
-  }
-
-  switch (message.name) {
-    case "Marionette:log":
-      // log server-side messages
-      logger.info(message.json.message);
-      break;
-
-    case "Marionette:shareData":
-      // log messages from tests
-      if (message.json.log) {
-        this.marionetteLog.addLogs(message.json.log);
-      }
-      break;
-
-    case "Marionette:runEmulatorCmd":
-    case "Marionette:runEmulatorShell":
-      this.emulator.send(message.json);
-      break;
-
-    case "Marionette:switchToModalOrigin":
-      this.curBrowser.frameManager.switchToModalOrigin(message);
-      this.mm = this.curBrowser.frameManager
-          .currentRemoteFrame.messageManager.get();
-      break;
-
-    case "Marionette:switchedToFrame":
-      if (message.json.restorePrevious) {
-        this.currentFrameElement = this.previousFrameElement;
-      } else {
-        // we don't arbitrarily save previousFrameElement, since
-        // we allow frame switching after modals appear, which would
-        // override this value and we'd lose our reference
-        if (message.json.storePrevious) {
-          this.previousFrameElement = this.currentFrameElement;
-        }
-        this.currentFrameElement = message.json.frameValue;
-      }
-      break;
-
-    case "Marionette:getVisibleCookies":
-      let [currentPath, host] = message.json.value;
-      let isForCurrentPath = path => currentPath.indexOf(path) != -1;
-      let results = [];
-
-      let en = cookieManager.enumerator;
-      while (en.hasMoreElements()) {
-        let cookie = en.getNext().QueryInterface(Ci.nsICookie);
-        // take the hostname and progressively shorten
-        let hostname = host;
-        do {
-          if ((cookie.host == "." + hostname || cookie.host == hostname) &&
-              isForCurrentPath(cookie.path)) {
-            results.push({
-              "name": cookie.name,
-              "value": cookie.value,
-              "path": cookie.path,
-              "host": cookie.host,
-              "secure": cookie.isSecure,
-              "expiry": cookie.expires
-            });
-            break;
-          }
-          hostname = hostname.replace(/^.*?\./, "");
-        } while (hostname.indexOf(".") != -1);
-      }
-      return results;
-
-    case "Marionette:addCookie":
-      let cookieToAdd = message.json.value;
-      Services.cookies.add(
-          cookieToAdd.domain,
-          cookieToAdd.path,
-          cookieToAdd.name,
-          cookieToAdd.value,
-          cookieToAdd.secure,
-          false,
-          false,
-          cookieToAdd.expiry);
-      return true;
-
-    case "Marionette:deleteCookie":
-      let cookieToDelete = message.json.value;
-      cookieManager.remove(
-          cookieToDelete.host,
-          cookieToDelete.name,
-          cookieToDelete.path,
-          false);
-      return true;
-
-    case "Marionette:emitTouchEvent":
-      globalMessageManager.broadcastAsyncMessage(
-          "MarionetteMainListener:emitTouchEvent", message.json);
-      break;
-
-    case "Marionette:register":
-      let wid = message.json.value;
-      let be = message.target;
-      let rv = this.registerBrowser(wid, be);
-      return rv;
-
-    case "Marionette:listenersAttached":
-      if (message.json.listenerId === this.curBrowser.curFrameId) {
-        // If remoteness gets updated we need to call newSession. In the case
-        // of desktop this just sets up a small amount of state that doesn't
-        // change over the course of a session.
-        let newSessionValues = {
-          B2G: (this.appName == "B2G"),
-          raisesAccessibilityExceptions:
-              this.sessionCapabilities.raisesAccessibilityExceptions
-        };
-        this.sendAsync("newSession", newSessionValues);
-        this.curBrowser.flushPendingCommands();
-      }
-      break;
-  }
-};
-
-GeckoDriver.prototype.responseCompleted = function () {
-  if (this.curBrowser !== null) {
-    this.curBrowser.pendingCommands = [];
-  }
-};
-
-GeckoDriver.prototype.commands = {
-  "getMarionetteID": GeckoDriver.prototype.getMarionetteID,
-  "sayHello": GeckoDriver.prototype.sayHello,
-  "newSession": GeckoDriver.prototype.newSession,
-  "getSessionCapabilities": GeckoDriver.prototype.getSessionCapabilities,
-  "log": GeckoDriver.prototype.log,
-  "getLogs": GeckoDriver.prototype.getLogs,
-  "setContext": GeckoDriver.prototype.setContext,
-  "getContext": GeckoDriver.prototype.getContext,
-  "executeScript": GeckoDriver.prototype.execute,
-  "setScriptTimeout": GeckoDriver.prototype.setScriptTimeout,
-  "timeouts": GeckoDriver.prototype.timeouts,
-  "singleTap": GeckoDriver.prototype.singleTap,
-  "actionChain": GeckoDriver.prototype.actionChain,
-  "multiAction": GeckoDriver.prototype.multiAction,
-  "executeAsyncScript": GeckoDriver.prototype.executeWithCallback,
-  "executeJSScript": GeckoDriver.prototype.executeJSScript,
-  "setSearchTimeout": GeckoDriver.prototype.setSearchTimeout,
-  "findElement": GeckoDriver.prototype.findElement,
-  "findChildElement": GeckoDriver.prototype.findChildElements, // Needed for WebDriver compat
-  "findElements": GeckoDriver.prototype.findElements,
-  "findChildElements":GeckoDriver.prototype.findChildElements, // Needed for WebDriver compat
-  "clickElement": GeckoDriver.prototype.clickElement,
-  "getElementAttribute": GeckoDriver.prototype.getElementAttribute,
-  "getElementText": GeckoDriver.prototype.getElementText,
-  "getElementTagName": GeckoDriver.prototype.getElementTagName,
-  "isElementDisplayed": GeckoDriver.prototype.isElementDisplayed,
-  "getElementValueOfCssProperty": GeckoDriver.prototype.getElementValueOfCssProperty,
-  "submitElement": GeckoDriver.prototype.submitElement,
-  "getElementSize": GeckoDriver.prototype.getElementSize,  //deprecated
-  "getElementRect": GeckoDriver.prototype.getElementRect,
-  "isElementEnabled": GeckoDriver.prototype.isElementEnabled,
-  "isElementSelected": GeckoDriver.prototype.isElementSelected,
-  "sendKeysToElement": GeckoDriver.prototype.sendKeysToElement,
-  "getElementLocation": GeckoDriver.prototype.getElementLocation,  // deprecated
-  "getElementPosition": GeckoDriver.prototype.getElementLocation,  // deprecated
-  "clearElement": GeckoDriver.prototype.clearElement,
-  "getTitle": GeckoDriver.prototype.getTitle,
-  "getWindowType": GeckoDriver.prototype.getWindowType,
-  "getPageSource": GeckoDriver.prototype.getPageSource,
-  "get": GeckoDriver.prototype.get,
-  "goUrl": GeckoDriver.prototype.get,  // deprecated
-  "getCurrentUrl": GeckoDriver.prototype.getCurrentUrl,
-  "getUrl": GeckoDriver.prototype.getCurrentUrl,  // deprecated
-  "goBack": GeckoDriver.prototype.goBack,
-  "goForward": GeckoDriver.prototype.goForward,
-  "refresh":  GeckoDriver.prototype.refresh,
-  "getWindowHandle": GeckoDriver.prototype.getWindowHandle,
-  "getCurrentWindowHandle":  GeckoDriver.prototype.getWindowHandle,  // Selenium 2 compat
-  "getChromeWindowHandle": GeckoDriver.prototype.getChromeWindowHandle,
-  "getCurrentChromeWindowHandle": GeckoDriver.prototype.getChromeWindowHandle,
-  "getWindow":  GeckoDriver.prototype.getWindowHandle,  // deprecated
-  "getWindowHandles": GeckoDriver.prototype.getWindowHandles,
-  "getChromeWindowHandles": GeckoDriver.prototype.getChromeWindowHandles,
-  "getCurrentWindowHandles": GeckoDriver.prototype.getWindowHandles,  // Selenium 2 compat
-  "getWindows":  GeckoDriver.prototype.getWindowHandles,  // deprecated
-  "getWindowPosition": GeckoDriver.prototype.getWindowPosition,
-  "setWindowPosition": GeckoDriver.prototype.setWindowPosition,
-  "getActiveFrame": GeckoDriver.prototype.getActiveFrame,
-  "switchToFrame": GeckoDriver.prototype.switchToFrame,
-  "switchToWindow": GeckoDriver.prototype.switchToWindow,
-  "deleteSession": GeckoDriver.prototype.deleteSession,
-  "importScript": GeckoDriver.prototype.importScript,
-  "clearImportedScripts": GeckoDriver.prototype.clearImportedScripts,
-  "getAppCacheStatus": GeckoDriver.prototype.getAppCacheStatus,
-  "close": GeckoDriver.prototype.close,
-  "closeWindow": GeckoDriver.prototype.close,  // deprecated
-  "closeChromeWindow": GeckoDriver.prototype.closeChromeWindow,
-  "setTestName": GeckoDriver.prototype.setTestName,
-  "takeScreenshot": GeckoDriver.prototype.takeScreenshot,
-  "screenShot": GeckoDriver.prototype.takeScreenshot,  // deprecated
-  "screenshot": GeckoDriver.prototype.takeScreenshot,  // Selenium 2 compat
-  "addCookie": GeckoDriver.prototype.addCookie,
-  "getCookies": GeckoDriver.prototype.getCookies,
-  "getAllCookies": GeckoDriver.prototype.getCookies,  // deprecated
-  "deleteAllCookies": GeckoDriver.prototype.deleteAllCookies,
-  "deleteCookie": GeckoDriver.prototype.deleteCookie,
-  "getActiveElement": GeckoDriver.prototype.getActiveElement,
-  "getScreenOrientation": GeckoDriver.prototype.getScreenOrientation,
-  "setScreenOrientation": GeckoDriver.prototype.setScreenOrientation,
-  "getWindowSize": GeckoDriver.prototype.getWindowSize,
-  "setWindowSize": GeckoDriver.prototype.setWindowSize,
-  "maximizeWindow": GeckoDriver.prototype.maximizeWindow,
-  "dismissDialog": GeckoDriver.prototype.dismissDialog,
-  "acceptDialog": GeckoDriver.prototype.acceptDialog,
-  "getTextFromDialog": GeckoDriver.prototype.getTextFromDialog,
-  "sendKeysToDialog": GeckoDriver.prototype.sendKeysToDialog
-};
-
-/**
- * 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.
- * @param {GeckoDriver} driver
- *     Reference to the driver the browser is attached to.
- */
-let BrowserObj = function(win, driver) {
-  this.browser = undefined;
-  this.window = win;
-  this.driver = driver;
-  this.knownFrames = [];
-  this.startPage = "about:blank";
-  // used in B2G to identify the homescreen content page
-  this.mainContentId = null;
-  // used to set curFrameId upon new session
-  this.newSession = true;
-  this.elementManager = new ElementManager([NAME, LINK_TEXT, PARTIAL_LINK_TEXT]);
-  this.setBrowser(win);
-
-  // A reference to the tab corresponding to the current window handle, if any.
-  this.tab = null;
-  this.pendingCommands = [];
-
-  // we should have one FM per BO so that we can handle modals in each Browser
-  this.frameManager = new FrameManager(driver);
-  this.frameRegsPending = 0;
-
-  // register all message listeners
-  this.frameManager.addMessageManagerListeners(driver.mm);
-  this.getIdForBrowser = driver.getIdForBrowser.bind(driver);
-  this.updateIdForBrowser = driver.updateIdForBrowser.bind(driver);
-  this._curFrameId = null;
-  this._browserWasRemote = null;
-  this._hasRemotenessChange = false;
-};
-
-Object.defineProperty(BrowserObj.prototype, "browserForTab", {
-  get() {
-    return this.browser.getBrowserForTab(this.tab);
-  }
-});
-
-/**
- * The current frame ID is managed per browser element on desktop in
- * case the ID needs to be refreshed. The currently selected window is
- * identified within BrowserObject by a tab.
- */
-Object.defineProperty(BrowserObj.prototype, "curFrameId", {
-  get() {
-    let rv = null;
-    if (this.driver.appName != "Firefox") {
-      rv = this._curFrameId;
-    } else if (this.tab) {
-      rv = this.getIdForBrowser(this.browserForTab);
-    }
-    return rv;
-  },
-
-  set(id) {
-    if (this.driver.appName != "Firefox") {
-      this._curFrameId = id;
-    }
-  }
-});
-
-/**
- * Retrieves the current tabmodal UI object.  According to the browser
- * associated with the currently selected tab.
- */
-BrowserObj.prototype.getTabModalUI = function() {
-  let br = this.browserForTab;
-  if (!br.hasAttribute("tabmodalPromptShowing")) {
-    return null;
-  }
-
-  // The modal is a direct sibling of the browser element.
-  // See tabbrowser.xml's getTabModalPromptBox.
-  let modals = br.parentNode.getElementsByTagNameNS(
-      XUL_NS, "tabmodalprompt");
-  return modals[0].ui;
-};
-
-/**
- * Set the browser if the application is not B2G.
- *
- * @param {nsIDOMWindow} win
- *     Current window reference.
- */
-BrowserObj.prototype.setBrowser = function(win) {
-  switch (this.driver.appName) {
-    case "Firefox":
-      this.browser = win.gBrowser;
-      break;
-
-    case "Fennec":
-      this.browser = win.BrowserApp;
-      break;
-
-    case "B2G":
-      // eideticker (bug 965297) and mochitest (bug 965304)
-      // compatibility.  They only check for the presence of this
-      // property and should not be in caps if not on a B2G device.
-      this.driver.sessionCapabilities.b2g = true;
-      break;
-  }
-};
-
-/** Called when we start a session with this browser. */
-BrowserObj.prototype.startSession = function(newSession, win, callback) {
-  callback(win, newSession);
-};
-
-/** Closes current tab. */
-BrowserObj.prototype.closeTab = function() {
-  if (this.browser &&
-      this.browser.removeTab &&
-      this.tab != null && (this.driver.appName != "B2G")) {
-    this.browser.removeTab(this.tab);
-  }
-};
-
-/**
- * Opens a tab with given URI.
- *
- * @param {string} uri
- *      URI to open.
- */
-BrowserObj.prototype.addTab = function(uri) {
-  return this.browser.addTab(uri, true);
-};
-
-/**
- * Re-sets this BrowserObject's current tab and updates remoteness tracking.
- */
-BrowserObj.prototype.switchToTab = function(ind) {
-  if (this.browser) {
-    this.browser.selectTabAtIndex(ind);
-    this.tab = this.browser.selectedTab;
-  }
-  this._browserWasRemote = this.browserForTab.isRemoteBrowser;
-  this._hasRemotenessChange = false;
-};
-
-/**
- * Registers a new frame, and sets its current frame id to this frame
- * if it is not already assigned, and if a) we already have a session
- * or b) we're starting a new session and it is the right start frame.
- *
- * @param {string} uid
- *     Frame uid for use by Marionette.
- * @param the XUL <browser> that was the target of the originating message.
- */
-BrowserObj.prototype.register = function(uid, target) {
-  let remotenessChange = this.hasRemotenessChange();
-  if (this.curFrameId === null || remotenessChange) {
-    if (this.browser) {
-      // If we're setting up a new session on Firefox, we only process the
-      // registration for this frame if it belongs to the current tab.
-      if (!this.tab) {
-        this.switchToTab(this.browser.selectedIndex);
-      }
-
-      if (target == this.browserForTab) {
-        this.updateIdForBrowser(this.browserForTab, uid);
-        this.mainContentId = uid;
-      }
-    } else {
-      this._curFrameId = uid;
-      this.mainContentId = uid;
-    }
-  }
-
-  // used to delete sessions
-  this.knownFrames.push(uid);
-  return remotenessChange;
-};
-
-/**
- * When navigating between pages results in changing a browser's
- * process, we need to take measures not to lose contact with a listener
- * script. This function does the necessary bookkeeping.
- */
-BrowserObj.prototype.hasRemotenessChange = function() {
-  // None of these checks are relevant on b2g or if we don't have a tab yet,
-  // and may not apply on Fennec.
-  if (this.driver.appName != "Firefox" || this.tab === null) {
-    return false;
-  }
-
-  if (this._hasRemotenessChange) {
-    return true;
-  }
-
-  let currentIsRemote = this.browserForTab.isRemoteBrowser;
-  this._hasRemotenessChange = this._browserWasRemote !== currentIsRemote;
-  this._browserWasRemote = currentIsRemote;
-  return this._hasRemotenessChange;
-};
-
-/**
- * Flushes any pending commands queued when a remoteness change is being
- * processed and mark this remotenessUpdate as complete.
- */
-BrowserObj.prototype.flushPendingCommands = function() {
-  if (!this._hasRemotenessChange) {
-    return;
-  }
-
-  this._hasRemotenessChange = false;
-  this.pendingCommands.forEach(cb => cb());
-  this.pendingCommands = [];
-};
-
-/**
-  * This function intercepts commands interacting with content and queues
-  * or executes them as needed.
-  *
-  * No commands interacting with content are safe to process until
-  * the new listener script is loaded and registers itself.
-  * This occurs when a command whose effect is asynchronous (such
-  * as goBack) results in a remoteness change and new commands
-  * are subsequently posted to the server.
-  */
-BrowserObj.prototype.executeWhenReady = function(cb) {
-  if (this.hasRemotenessChange()) {
-    this.pendingCommands.push(cb);
-  } else {
-    cb();
-  }
-};
deleted file mode 100644
--- a/testing/marionette/emulator.js
+++ /dev/null
@@ -1,122 +0,0 @@
-/* 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";
-
-const {classes: Cc, interfaces: Ci} = Components;
-const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
-this.EXPORTED_SYMBOLS = ["emulator", "Emulator", "EmulatorCallback"];
-
-this.emulator = {};
-
-/**
- * Determines if command ID is an emulator callback.
- */
-this.emulator.isCallback = function(cmdId) {
-  return cmdId < 0;
-};
-
-/**
- * Represents the connection between Marionette and the emulator it's
- * running on.
- *
- * When injected scripts call the JS routines {@code runEmulatorCmd} or
- * {@code runEmulatorShell}, the second argument to those is a callback
- * which is stored in cbs.  They are later retreived by their unique ID
- * using popCallback.
- *
- * @param {function(Object)} sendFn
- *     Callback function that sends a message to the emulator.
- */
-this.Emulator = function(sendFn) {
-  this.send = sendFn;
-  this.cbs = [];
-};
-
-/**
- * Pops a callback off the stack if found.  Otherwise this is a no-op.
- *
- * @param {number} id
- *     Unique ID associated with the callback.
- *
- * @return {?function(Object)}
- *     Callback function that takes an emulator response message as
- *     an argument.
- */
-Emulator.prototype.popCallback = function(id) {
-  let f, fi;
-  for (let i = 0; i < this.cbs.length; ++i) {
-    if (this.cbs[i].id == id) {
-      f = this.cbs[i];
-      fi = i;
-    }
-  }
-
-  if (!f) {
-    return null;
-  }
-
-  this.cbs.splice(fi, 1);
-  return f;
-};
-
-/**
- * Pushes callback on to the stack.
- *
- * @param {function(Object)} cb
- *     Callback function that takes an emulator response message as
- *     an argument.
- */
-Emulator.prototype.pushCallback = function(cb) {
-  cb.send_ = this.sendFn;
-  this.cbs.push(cb);
-};
-
-/**
- * Encapsulates a callback to the emulator and provides an execution
- * environment for them.
- *
- * Each callback is assigned a unique identifier, id, that can be used
- * to retrieve them from Emulator's stack using popCallback.
- *
- * The onresult event listener is triggered when a result arrives on
- * the callback.
- *
- * The onerror event listener is triggered when an error occurs during
- * the execution of that callback.
- */
-this.EmulatorCallback = function() {
-  this.id = uuidGen.generateUUID().toString();
-  this.onresult = null;
-  this.onerror = null;
-  this.send_ = null;
-};
-
-EmulatorCallback.prototype.command = function(cmd, cb) {
-  this.onresult = cb;
-  this.send_({emulator_cmd: cmd, id: this.id});
-};
-
-EmulatorCallback.prototype.shell = function(args, cb) {
-  this.onresult = cb;
-  this.send_({emulator_shell: args, id: this.id});
-};
-
-EmulatorCallback.prototype.result = function(msg) {
-  if (this.send_ === null) {
-    throw new TypeError(
-      "EmulatorCallback must be registered with Emulator to fire");
-  }
-
-  try {
-    if (!this.onresult) {
-      return;
-    }
-    this.onresult(msg.result);
-  } catch (e) {
-    if (this.onerror) {
-      this.onerror(e);
-    }
-  }
-};
deleted file mode 100644
--- a/testing/marionette/error.js
+++ /dev/null
@@ -1,314 +0,0 @@
-/* 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";
-
-const {utils: Cu} = Components;
-
-const errors = [
-  "ElementNotVisibleError",
-  "FrameSendFailureError",
-  "FrameSendNotInitializedError",
-  "JavaScriptError",
-  "NoAlertOpenError",
-  "NoSuchElementError",
-  "NoSuchFrameError",
-  "NoSuchWindowError",
-  "ScriptTimeoutError",
-  "SessionNotCreatedError",
-  "TimeoutError",
-  "UnknownCommandError",
-  "UnknownError",
-  "UnsupportedOperationError",
-  "WebDriverError",
-];
-
-this.EXPORTED_SYMBOLS = ["error"].concat(errors);
-
-this.error = {};
-
-error.toJSON = function(err) {
-  return {
-    message: err.message,
-    stacktrace: err.stack || null,
-    status: err.code
-  };
-};
-
-/**
- * Gets WebDriver error by its Selenium status code number.
- */
-error.byCode = n => lookup.get(n);
-
-/**
- * Determines if the given status code is successful.
- */
-error.isSuccess = code => code === 0;
-
-/**
- * Old-style errors are objects that has all of the properties
- * "message", "code", and "stack".
- *
- * When listener.js starts forwarding real errors by CPOW
- * we can remove this.
- */
-let isOldStyleError = function(obj) {
-  return typeof obj == "object" &&
-    ["message", "code", "stack"].every(c => obj.hasOwnProperty(c));
-}
-
-/**
- * Checks if obj is an instance of the Error prototype in a safe manner.
- * Prefer using this over using instanceof since the Error prototype
- * isn't unique across browsers, and XPCOM exceptions are special
- * snowflakes.
- */
-error.isError = function(obj) {
-  if (obj === null || typeof obj != "object") {
-    return false;
-  // XPCOM exception.
-  // Object.getPrototypeOf(obj).result throws error,
-  // consequently we must do the check for properties in its
-  // prototypal chain (using in, instead of obj.hasOwnProperty) here.
-  } else if ("result" in obj) {
-    return true;
-  } else {
-    return Object.getPrototypeOf(obj) == "Error" || isOldStyleError(obj);
-  }
-};
-
-/**
- * Checks if obj is an object in the WebDriverError prototypal chain.
- */
-error.isWebDriverError = function(obj) {
-  return error.isError(obj) &&
-    (("name" in obj && errors.indexOf(obj.name) > 0) ||
-      isOldStyleError(obj));
-};
-
-/**
- * Unhandled error reporter.  Dumps the error and its stacktrace to console,
- * and reports error to the Browser Console.
- */
-error.report = function(err) {
-  let msg = `Marionette threw an error: ${error.stringify(err)}`;
-  dump(msg + "\n");
-  if (Cu.reportError) {
-    Cu.reportError(msg);
-  }
-};
-
-/**
- * Prettifies an instance of Error and its stacktrace to a string.
- */
-error.stringify = function(err) {
-  try {
-    let s = err.toString();
-    if ("stack" in err) {
-      s += "\n" + err.stack;
-    }
-    return s;
-  } catch (e) {
-    return "<unprintable error>";
-  }
-};
-
-/**
- * WebDriverError is the prototypal parent of all WebDriver errors.
- * It should not be used directly, as it does not correspond to a real
- * error in the specification.
- */
-this.WebDriverError = function(msg) {
-  Error.call(this, msg);
-  this.name = "WebDriverError";
-  this.message = msg;
-  this.code = 500;  // overridden
-};
-WebDriverError.prototype = Object.create(Error.prototype);
-
-this.NoSuchElementError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "NoSuchElementError";
-  this.status = "no such element";
-  this.code = 7;
-};
-NoSuchElementError.prototype = Object.create(WebDriverError.prototype);
-
-this.NoSuchFrameError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "NoSuchFrameError";
-  this.status = "no such frame";
-  this.code = 8;
-};
-NoSuchFrameError.prototype = Object.create(WebDriverError.prototype);
-
-this.UnknownCommandError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "UnknownCommandError";
-  this.status = "unknown command";
-  this.code = 9;
-};
-UnknownCommandError.prototype = Object.create(WebDriverError.prototype);
-
-this.ElementNotVisibleError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "ElementNotVisibleError";
-  this.status = "element not visible";
-  this.code = 11;
-};
-ElementNotVisibleError.prototype = Object.create(WebDriverError.prototype);
-
-this.InvalidElementState = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "InvalidElementState";
-  this.status = "invalid element state";
-  this.code = 12;
-};
-InvalidElementState.prototype = Object.create(WebDriverError.prototype);
-
-this.UnknownError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "UnknownError";
-  this.status = "unknown error";
-  this.code = 13;
-};
-UnknownError.prototype = Object.create(WebDriverError.prototype);
-
-/**
- * Creates an error message for a JavaScript error thrown during
- * executeScript or executeAsyncScript.
- *
- * @param {Error} err
- *     An Error object passed to a catch block or a message.
- * @param {string} fnName
- *     The name of the function to use in the stack trace message
- *     (e.g. execute_script).
- * @param {string} file
- *     The filename of the test file containing the Marionette
- *     command that caused this error to occur.
- * @param {number} line
- *     The line number of the above test file.
- * @param {string=} script
- *     The JS script being executed in text form.
- */
-this.JavaScriptError = function(err, fnName, file, line, script) {
-  let msg = String(err);
-  let trace = "";
-
-  if (fnName && line) {
-    trace += `${fnName} @${file}`;
-    if (line) {
-      trace += `, line ${line}`;
-    }
-  }
-
-  if (typeof err == "object" && "name" in err && "stack" in err) {
-    let jsStack = err.stack.split("\n");
-    let match = jsStack[0].match(/:(\d+):\d+$/);
-    let jsLine = match ? parseInt(match[1]) : 0;
-    if (script) {
-      let src = script.split("\n")[jsLine];
-      trace += "\n" +
-        "inline javascript, line " + jsLine + "\n" +
-        "src: \"" + src + "\"";
-    }
-  }
-
-  WebDriverError.call(this, msg);
-  this.name = "JavaScriptError";
-  this.status = "javascript error";
-  this.code = 17;
-  this.stack = trace;
-};
-JavaScriptError.prototype = Object.create(WebDriverError.prototype);
-
-this.TimeoutError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "TimeoutError";
-  this.status = "timeout";
-  this.code = 21;
-};
-TimeoutError.prototype = Object.create(WebDriverError.prototype);
-
-this.NoSuchWindowError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "NoSuchWindowError";
-  this.status = "no such window";
-  this.code = 23;
-};
-NoSuchWindowError.prototype = Object.create(WebDriverError.prototype);
-
-this.NoAlertOpenError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "NoAlertOpenError";
-  this.status = "no such alert";
-  this.code = 27;
-}
-NoAlertOpenError.prototype = Object.create(WebDriverError.prototype);
-
-this.ScriptTimeoutError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "ScriptTimeoutError";
-  this.status = "script timeout";
-  this.code = 28;
-};
-ScriptTimeoutError.prototype = Object.create(WebDriverError.prototype);
-
-this.SessionNotCreatedError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "SessionNotCreatedError";
-  this.status = "session not created";
-  // should be 33 to match Selenium
-  this.code = 71;
-}
-SessionNotCreatedError.prototype = Object.create(WebDriverError.prototype);
-
-this.FrameSendNotInitializedError = function(frame) {
-  this.message = "Error sending message to frame (NS_ERROR_NOT_INITIALIZED)";
-  WebDriverError.call(this, this.message);
-  this.name = "FrameSendNotInitializedError";
-  this.status = "frame send not initialized error";
-  this.code = 54;
-  this.frame = frame;
-  this.errMsg = `${this.message} ${this.frame}; frame has closed.`;
-};
-FrameSendNotInitializedError.prototype = Object.create(WebDriverError.prototype);
-
-this.FrameSendFailureError = function(frame) {
-  this.message = "Error sending message to frame (NS_ERROR_FAILURE)";
-  WebDriverError.call(this, this.message);
-  this.name = "FrameSendFailureError";
-  this.status = "frame send failure error";
-  this.code = 55;
-  this.frame = frame;
-  this.errMsg = `${this.message} ${this.frame}; frame not responding.`;
-};
-FrameSendFailureError.prototype = Object.create(WebDriverError.prototype);
-
-this.UnsupportedOperationError = function(msg) {
-  WebDriverError.call(this, msg);
-  this.name = "UnsupportedOperationError";
-  this.status = "unsupported operation";
-  this.code = 405;
-};
-UnsupportedOperationError.prototype = Object.create(WebDriverError.prototype);
-
-const errorObjs = [
-  this.ElementNotVisibleError,
-  this.FrameSendFailureError,
-  this.FrameSendNotInitializedError,
-  this.JavaScriptError,
-  this.NoAlertOpenError,
-  this.NoSuchElementError,
-  this.NoSuchFrameError,
-  this.NoSuchWindowError,
-  this.ScriptTimeoutError,
-  this.SessionNotCreatedError,
-  this.TimeoutError,
-  this.UnknownCommandError,
-  this.UnknownError,
-  this.UnsupportedOperationError,
-  this.WebDriverError,
-];
-const lookup = new Map(errorObjs.map(err => [new err().code, err]));
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -1,30 +1,24 @@
 # 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/server.js (server.js)
-  content/driver.js (driver.js)
-  content/actions.js (actions.js)
-  content/listener.js (listener.js)
-  content/elements.js (elements.js)
-  content/sendkeys.js (sendkeys.js)
-  content/common.js (common.js)
-  content/simpletest.js (simpletest.js)
-  content/frame-manager.js (frame-manager.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-common.js      (marionette-common.js)
+  content/marionette-actions.js     (marionette-actions.js)
+  content/marionette-simpletest.js  (marionette-simpletest.js)
+  content/marionette-frame-manager.js  (marionette-frame-manager.js)
   content/EventUtils.js  (EventUtils.js)
   content/ChromeUtils.js  (ChromeUtils.js)
-  content/error.js (error.js)
-  content/command.js (command.js)
-  content/dispatcher.js (dispatcher.js)
-  content/emulator.js (emulator.js)
-  content/modal.js (modal.js)
 #ifdef ENABLE_TESTS
   content/test.xul  (client/marionette/chrome/test.xul)
   content/test2.xul  (client/marionette/chrome/test2.xul)
   content/test_nested_iframe.xul  (client/marionette/chrome/test_nested_iframe.xul)
   content/test_anonymous_content.xul  (client/marionette/chrome/test_anonymous_content.xul)
 #endif
 
 % content specialpowers %content/
rename from testing/marionette/actions.js
rename to testing/marionette/marionette-actions.js
--- a/testing/marionette/actions.js
+++ b/testing/marionette/marionette-actions.js
@@ -1,492 +1,382 @@
 /* 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/. */
 
-this.EXPORTED_SYMBOLS = ["ActionChain"];
-
 /**
  * Functionality for (single finger) action chains.
  */
-this.ActionChain = function(utils, checkForInterrupted) {
-  // for assigning unique ids to all touches
+this.ActionChain = function (utils, checkForInterrupted) {
+  // For assigning unique ids to all touches
   this.nextTouchId = 1000;
-  // keep track of active Touches
+  // Keep track of active Touches
   this.touchIds = {};
   // last touch for each fingerId
   this.lastCoordinates = null;
   this.isTap = false;
   this.scrolling = false;
   // whether to send mouse event
   this.mouseEventsOnly = false;
   this.checkTimer = Components.classes["@mozilla.org/timer;1"]
-      .createInstance(Components.interfaces.nsITimer);
+                              .createInstance(Components.interfaces.nsITimer);
 
-  // callbacks for command completion
+  // Callbacks for command completion.
   this.onSuccess = null;
   this.onError = null;
   if (typeof checkForInterrupted == "function") {
     this.checkForInterrupted = checkForInterrupted;
   } else {
     this.checkForInterrupted = () => {};
   }
 
-  // determines if we create touch events
+  // Determines if we create touch events.
   this.inputSource = null;
 
-  // test utilities providing some event synthesis code
+  // Test utilities providing some event synthesis code.
   this.utils = utils;
-};
+}
 
-ActionChain.prototype.dispatchActions = function(
-    args,
-    touchId,
-    frame,
-    elementManager,
-    callbacks,
-    touchProvider) {
-  // Some touch events code in the listener needs to do ipc, so we can't
-  // share this code across chrome/content.
-  if (touchProvider) {
-    this.touchProvider = touchProvider;
-  }
+ActionChain.prototype = {
 
-  this.elementManager = elementManager;
-  let commandArray = elementManager.convertWrappedArguments(args, frame);
-  this.onSuccess = callbacks.onSuccess;
-  this.onError = callbacks.onError;
-  this.frame = frame;
-
-  if (touchId == null) {
-    touchId = this.nextTouchId++;
-  }
-
-  if (!frame.document.createTouch) {
-    this.mouseEventsOnly = true;
-  }
-
-  let keyModifiers = {
-    shiftKey: false,
-    ctrlKey: false,
-    altKey: false,
-    metaKey: false
-  };
+  dispatchActions: function (args, touchId, frame, elementManager, callbacks,
+                             touchProvider) {
+    // Some touch events code in the listener needs to do ipc, so we can't
+    // share this code across chrome/content.
+    if (touchProvider) {
+      this.touchProvider = touchProvider;
+    }
 
-  try {
-    this.actions(commandArray, touchId, 0, keyModifiers);
-  } catch (e) {
-    this.onError(e);
-    this.resetValues();
-  }
-};
+    this.elementManager = elementManager;
+    let commandArray = elementManager.convertWrappedArguments(args, frame);
+    let {onSuccess, onError} = callbacks;
+    this.onSuccess = onSuccess;
+    this.onError = onError;
+    this.frame = frame;
 
-/**
- * This function emit mouse event.
- *
- * @param {Document} doc
- *     Current document.
- * @param {string} type
- *     Type of event to dispatch.
- * @param {number} clickCount
- *     Number of clicks, button notes the mouse button.
- * @param {number} elClientX
- *     X coordinate of the mouse relative to the viewport.
- * @param {number} elClientY
- *     Y coordinate of the mouse relative to the viewport.
- * @param {Object} modifiers
- *     An object of modifier keys present.
- */
-ActionChain.prototype.emitMouseEvent = function(
-    doc,
-    type,
-    elClientX,
-    elClientY,
-    button,
-    clickCount,
-    modifiers) {
-  if (!this.checkForInterrupted()) {
-    let loggingInfo = "emitting Mouse event of type " + type +
-      " at coordinates (" + elClientX + ", " + elClientY +
-      ") relative to the viewport\n" +
-      " button: " + button + "\n" +
-      " clickCount: " + clickCount + "\n";
-    dump(Date.now() + " Marionette: " + loggingInfo);
+    if (touchId == null) {
+      touchId = this.nextTouchId++;
+    }
 
-    let win = doc.defaultView;
-    let domUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-        .getInterface(Components.interfaces.nsIDOMWindowUtils);
-
-    let mods;
-    if (typeof modifiers != "undefined") {
-      mods = this.utils._parseModifiers(modifiers);
-    } else {
-      mods = 0;
+    if (!frame.document.createTouch) {
+      this.mouseEventsOnly = true;
     }
 
-    domUtils.sendMouseEvent(
-        type,
-        elClientX,
-        elClientY,
-        button || 0, 
-        clickCount || 1,
-        mods,
-        false,
-        0,
-        this.inputSource);
-  }
-};
+    let keyModifiers = {
+      shiftKey: false,
+      ctrlKey: false,
+      altKey: false,
+      metaKey: false
+    };
 
-/**
- * Reset any persisted values after a command completes.
- */
-ActionChain.prototype.resetValues = function() {
-  this.onSuccess = null;
-  this.onError = null;
-  this.frame = null;
-  this.elementManager = null;
-  this.touchProvider = null;
-  this.mouseEventsOnly = false;
-};
+    try {
+      this.actions(commandArray, touchId, 0, keyModifiers);
+    } catch (e) {
+      this.onError(e.message, e.code, e.stack);
+      this.resetValues();
+    }
+  },
 
-/**
- * Function to emit touch events for each finger. e.g.
- * finger=[['press', id], ['wait', 5], ['release']] touchId represents
- * the finger id, i keeps track of the current action of the chain
- * keyModifiers is an object keeping track keyDown/keyUp pairs through
- * an action chain.
- */
-ActionChain.prototype.actions = function(chain, touchId, i, keyModifiers) {
-  if (i == chain.length) {
-    this.onSuccess({value: touchId});
-    this.resetValues();
-    return;
-  }
+  /**
+   * This function emit mouse event
+   *   @param: doc is the current document
+   *           type is the type of event to dispatch
+   *           clickCount is the number of clicks, button notes the mouse button
+   *           elClientX and elClientY are the coordinates of the mouse relative to the viewport
+   *           modifiers is an object of modifier keys present
+   */
+  emitMouseEvent: function (doc, type, elClientX, elClientY, button, clickCount, modifiers) {
+    if (!this.checkForInterrupted()) {
+      let loggingInfo = "emitting Mouse event of type " + type +
+        " at coordinates (" + elClientX + ", " + elClientY +
+        ") relative to the viewport\n" +
+        " button: " + button + "\n" +
+        " clickCount: " + clickCount + "\n";
+      dump(Date.now() + " Marionette: " + loggingInfo);
+      let win = doc.defaultView;
+      let domUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+        .getInterface(Components.interfaces.nsIDOMWindowUtils);
+      let mods;
+      if (typeof modifiers != "undefined") {
+        mods = this.utils._parseModifiers(modifiers);
+      } else {
+        mods = 0;
+      }
+      domUtils.sendMouseEvent(type, elClientX, elClientY, button || 0, clickCount || 1,
+                              mods, false, 0, this.inputSource);
+    }
+  },
 
-  let pack = chain[i];
-  let command = pack[0];
-  let el;
-  let c;
-  i++;
+  /**
+   * Reset any persisted values after a command completes.
+   */
+  resetValues: function () {
+    this.onSuccess = null;
+    this.onError = null;
+    this.frame = null;
+    this.elementManager = null;
+    this.touchProvider = null;
+    this.mouseEventsOnly = false;
+  },
+
+  /**
+   * Function to emit touch events for each finger. e.g. finger=[['press', id], ['wait', 5], ['release']]
+   * touchId represents the finger id, i keeps track of the current action of the chain
+   * keyModifiers is an object keeping track keyDown/keyUp pairs through an action chain.
+   */
+  actions: function (chain, touchId, i, keyModifiers) {
 
-  if (["press", "wait", "keyDown", "keyUp", "click"].indexOf(command) == -1) {
-    // if mouseEventsOnly, then touchIds isn't used
-    if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
+    if (i == chain.length) {
+      this.onSuccess({value: touchId});
       this.resetValues();
-      throw new WebDriverError("Element has not been pressed");
+      return;
     }
-  }
+
+    let pack = chain[i];
+    let command = pack[0];
+    let el;
+    let c;
+    i++;
 
-  switch(command) {
-    case "keyDown":
+    if (['press', 'wait', 'keyDown', 'keyUp', 'click'].indexOf(command) == -1) {
+      // if mouseEventsOnly, then touchIds isn't used
+      if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
+        this.onError("Element has not been pressed", 500, null);
+        this.resetValues();
+        return;
+      }
+    }
+
+    switch(command) {
+    case 'keyDown':
       this.utils.sendKeyDown(pack[1], keyModifiers, this.frame);
       this.actions(chain, touchId, i, keyModifiers);
       break;
-
-    case "keyUp":
+    case 'keyUp':
       this.utils.sendKeyUp(pack[1], keyModifiers, this.frame);
       this.actions(chain, touchId, i, keyModifiers);
       break;
-
-    case "click":
+    case 'click':
       el = this.elementManager.getKnownElement(pack[1], this.frame);
       let button = pack[2];
       let clickCount = pack[3];
       c = this.coordinates(el, null, null);
-      this.mouseTap(el.ownerDocument, c.x, c.y, button, clickCount, keyModifiers);
+      this.mouseTap(el.ownerDocument, c.x, c.y, button, clickCount,
+                    keyModifiers);
       if (button == 2) {
-        this.emitMouseEvent(el.ownerDocument, "contextmenu", c.x, c.y,
-            button, clickCount, keyModifiers);
+        this.emitMouseEvent(el.ownerDocument, 'contextmenu', c.x, c.y,
+                            button, clickCount, keyModifiers);
       }
       this.actions(chain, touchId, i, keyModifiers);
       break;
-
-    case "press":
+    case 'press':
       if (this.lastCoordinates) {
-        this.generateEvents(
-            "cancel",
-            this.lastCoordinates[0],
-            this.lastCoordinates[1],
-            touchId,
-            null,
-            keyModifiers);
+        this.generateEvents('cancel', this.lastCoordinates[0], this.lastCoordinates[1],
+                            touchId, null, keyModifiers);
+        this.onError("Invalid Command: press cannot follow an active touch event", 500, null);
         this.resetValues();
-        throw new WebDriverError(
-            "Invalid Command: press cannot follow an active touch event");
+        return;
       }
-
       // look ahead to check if we're scrolling. Needed for APZ touch dispatching.
       if ((i != chain.length) && (chain[i][0].indexOf('move') !== -1)) {
         this.scrolling = true;
       }
       el = this.elementManager.getKnownElement(pack[1], this.frame);
       c = this.coordinates(el, pack[2], pack[3]);
-      touchId = this.generateEvents("press", c.x, c.y, null, el, keyModifiers);
+      touchId = this.generateEvents('press', c.x, c.y, null, el, keyModifiers);
       this.actions(chain, touchId, i, keyModifiers);
       break;
-
-    case "release":
-      this.generateEvents(
-          "release",
-          this.lastCoordinates[0],
-          this.lastCoordinates[1],
-          touchId,
-          null,
-          keyModifiers);
+    case 'release':
+      this.generateEvents('release', this.lastCoordinates[0], this.lastCoordinates[1],
+                          touchId, null, keyModifiers);
       this.actions(chain, null, i, keyModifiers);
       this.scrolling =  false;
       break;
-
-    case "move":
+    case 'move':
       el = this.elementManager.getKnownElement(pack[1], this.frame);
       c = this.coordinates(el);
-      this.generateEvents("move", c.x, c.y, touchId, null, keyModifiers);
+      this.generateEvents('move', c.x, c.y, touchId, null, keyModifiers);
       this.actions(chain, touchId, i, keyModifiers);
       break;
-
-    case "moveByOffset":
-      this.generateEvents(
-          "move",
-          this.lastCoordinates[0] + pack[1],
-          this.lastCoordinates[1] + pack[2],
-          touchId,
-          null,
-          keyModifiers);
+    case 'moveByOffset':
+      this.generateEvents('move', this.lastCoordinates[0] + pack[1],
+                          this.lastCoordinates[1] + pack[2],
+                          touchId, null, keyModifiers);
       this.actions(chain, touchId, i, keyModifiers);
       break;
-
-    case "wait":
-      if (pack[1] != null) {
-        let time = pack[1] * 1000;
-
+    case 'wait':
+      if (pack[1] != null ) {
+        let time = pack[1]*1000;
         // standard waiting time to fire contextmenu
         let standard = 750;
         try {
           standard = Services.prefs.getIntPref("ui.click_hold_context_menus.delay");
-        } catch (e) {}
-
+        }
+        catch (e){}
         if (time >= standard && this.isTap) {
-          chain.splice(i, 0, ["longPress"], ["wait", (time - standard) / 1000]);
+          chain.splice(i, 0, ['longPress'], ['wait', (time-standard)/1000]);
           time = standard;
         }
-        this.checkTimer.initWithCallback(
-            () => this.actions(chain, touchId, i, keyModifiers),
-            time, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
-      } else {
+        this.checkTimer.initWithCallback(() => {
+          this.actions(chain, touchId, i, keyModifiers);
+        }, time, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+      }
+      else {
         this.actions(chain, touchId, i, keyModifiers);
       }
       break;
-  
-    case "cancel":
-      this.generateEvents(
-          "cancel",
-          this.lastCoordinates[0],
-          this.lastCoordinates[1],
-          touchId,
-          null,
-          keyModifiers);
+    case 'cancel':
+      this.generateEvents('cancel', this.lastCoordinates[0], this.lastCoordinates[1],
+                          touchId, null, keyModifiers);
       this.actions(chain, touchId, i, keyModifiers);
       this.scrolling = false;
       break;
-  
-    case "longPress":
-      this.generateEvents(
-          "contextmenu",
-          this.lastCoordinates[0],
-          this.lastCoordinates[1],
-          touchId,
-          null,
-          keyModifiers);
+    case 'longPress':
+      this.generateEvents('contextmenu', this.lastCoordinates[0], this.lastCoordinates[1],
+                          touchId, null, keyModifiers);
       this.actions(chain, touchId, i, keyModifiers);
       break;
-
-  }
-};
+    }
+  },
 
-/**
- * This function generates a pair of coordinates relative to the viewport given a
- * target element and coordinates relative to that element's top-left corner.
- *
- * @param {DOMElement} target
- *     The target to calculate coordinates of.
- * @param {number} x
- *     X coordinate relative to target.  If unspecified, the centre of
- *     the target is used.
- * @param {number} y
- *     Y coordinate relative to target.  If unspecified, the centre of
- *     the target is used.
- */
-ActionChain.prototype.coordinates = function(target, x, y) {
-  let box = target.getBoundingClientRect();
-  if (x == null) {
-    x = box.width / 2;
-  }
-  if (y == null) {
-    y = box.height / 2;
-  }
-  let coords = {};
-  coords.x = box.left + x;
-  coords.y = box.top + y;
-  return coords;
-};
+  /**
+   * This function generates a pair of coordinates relative to the viewport given a
+   * target element and coordinates relative to that element's top-left corner.
+   * @param 'x', and 'y' are the relative to the target.
+   *        If they are not specified, then the center of the target is used.
+   */
+  coordinates: function (target, x, y) {
+    let box = target.getBoundingClientRect();
+    if (x == null) {
+      x = box.width / 2;
+    }
+    if (y == null) {
+      y = box.height / 2;
+    }
+    let coords = {};
+    coords.x = box.left + x;
+    coords.y = box.top + y;
+    return coords;
+  },
 
-/**
- * Given an element and a pair of coordinates, returns an array of the
- * form [clientX, clientY, pageX, pageY, screenX, screenY].
- */
-ActionChain.prototype.getCoordinateInfo = function(el, corx, cory) {
-  let win = el.ownerDocument.defaultView;
-  return [
-    corx, // clientX
-    cory, // clientY
-    corx + win.pageXOffset, // pageX
-    cory + win.pageYOffset, // pageY
-    corx + win.mozInnerScreenX, // screenX
-    cory + win.mozInnerScreenY // screenY
-  ];
-};
+  /**
+   * Given an element and a pair of coordinates, returns an array of the form
+   * [ clientX, clientY, pageX, pageY, screenX, screenY ]
+   */
+  getCoordinateInfo: function (el, corx, cory) {
+    let win = el.ownerDocument.defaultView;
+    return [ corx, // clientX
+             cory, // clientY
+             corx + win.pageXOffset, // pageX
+             cory + win.pageYOffset, // pageY
+             corx + win.mozInnerScreenX, // screenX
+             cory + win.mozInnerScreenY // screenY
+           ];
+  },
 
-/**
- * @param {number} x
- *     X coordinate of the location to generate the event that is relative
- *     to the viewport.
- * @param {number} y
- *     Y coordinate of the location to generate the event that is relative
- *     to the viewport.
- */
-ActionChain.prototype.generateEvents = function(
-    type, x, y, touchId, target, keyModifiers) {
-  this.lastCoordinates = [x, y];
-  let doc = this.frame.document;
-
-  switch (type) {
-    case "tap":
+  //x and y are coordinates relative to the viewport
+  generateEvents: function (type, x, y, touchId, target, keyModifiers) {
+    this.lastCoordinates = [x, y];
+    let doc = this.frame.document;
+    switch (type) {
+    case 'tap':
       if (this.mouseEventsOnly) {
-        this.mouseTap(
-            touch.target.ownerDocument,
-            touch.clientX,
-            touch.clientY,
-            null,
-            null,
-            keyModifiers);
+        this.mouseTap(touch.target.ownerDocument, touch.clientX, touch.clientY,
+                      null, null, keyModifiers);
       } else {
         touchId = this.nextTouchId++;
         let touch = this.touchProvider.createATouch(target, x, y, touchId);
-        this.touchProvider.emitTouchEvent("touchstart", touch);
-        this.touchProvider.emitTouchEvent("touchend", touch);
-        this.mouseTap(
-            touch.target.ownerDocument,
-            touch.clientX,
-            touch.clientY,
-            null,
-            null,
-            keyModifiers);
+        this.touchProvider.emitTouchEvent('touchstart', touch);
+        this.touchProvider.emitTouchEvent('touchend', touch);
+        this.mouseTap(touch.target.ownerDocument, touch.clientX, touch.clientY,
+                      null, null, keyModifiers);
       }
       this.lastCoordinates = null;
       break;
-
-    case "press":
+    case 'press':
       this.isTap = true;
       if (this.mouseEventsOnly) {
-        this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers);
-        this.emitMouseEvent(doc, "mousedown", x, y, null, null, keyModifiers);
-      } else {
+        this.emitMouseEvent(doc, 'mousemove', x, y, null, null, keyModifiers);
+        this.emitMouseEvent(doc, 'mousedown', x, y, null, null, keyModifiers);
+      }
+      else {
         touchId = this.nextTouchId++;
         let touch = this.touchProvider.createATouch(target, x, y, touchId);
-        this.touchProvider.emitTouchEvent("touchstart", touch);
+        this.touchProvider.emitTouchEvent('touchstart', touch);
         this.touchIds[touchId] = touch;
         return touchId;
       }
       break;
-
-    case "release":
+    case 'release':
       if (this.mouseEventsOnly) {
         let [x, y] = this.lastCoordinates;
-        this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers);
-      } else {
+        this.emitMouseEvent(doc, 'mouseup', x, y,
+                            null, null, keyModifiers);
+      }
+      else {
         let touch = this.touchIds[touchId];
         let [x, y] = this.lastCoordinates;
-
         touch = this.touchProvider.createATouch(touch.target, x, y, touchId);
-        this.touchProvider.emitTouchEvent("touchend", touch);
-
+        this.touchProvider.emitTouchEvent('touchend', touch);
         if (this.isTap) {
-          this.mouseTap(
-              touch.target.ownerDocument,
-              touch.clientX,
-              touch.clientY,
-              null,
-              null,
-              keyModifiers);
+          this.mouseTap(touch.target.ownerDocument, touch.clientX, touch.clientY,
+                        null, null, keyModifiers);
         }
         delete this.touchIds[touchId];
       }
-
       this.isTap = false;
       this.lastCoordinates = null;
       break;
-
-    case "cancel":
+    case 'cancel':
       this.isTap = false;
       if (this.mouseEventsOnly) {
         let [x, y] = this.lastCoordinates;
-        this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers);
-      } else {
-        this.touchProvider.emitTouchEvent("touchcancel", this.touchIds[touchId]);
+        this.emitMouseEvent(doc, 'mouseup', x, y,
+                            null, null, keyModifiers);
+      }
+      else {
+        this.touchProvider.emitTouchEvent('touchcancel', this.touchIds[touchId]);
         delete this.touchIds[touchId];
       }
       this.lastCoordinates = null;
       break;
-
-    case "move":
+    case 'move':
       this.isTap = false;
       if (this.mouseEventsOnly) {
-        this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers);
-      } else {
-        let touch = this.touchProvider.createATouch(
-            this.touchIds[touchId].target, x, y, touchId);
+        this.emitMouseEvent(doc, 'mousemove', x, y, null, null, keyModifiers);
+      }
+      else {
+        let touch = this.touchProvider.createATouch(this.touchIds[touchId].target,
+                                                    x, y, touchId);
         this.touchIds[touchId] = touch;
-        this.touchProvider.emitTouchEvent("touchmove", touch);
+        this.touchProvider.emitTouchEvent('touchmove', touch);
       }
       break;
-
-    case "contextmenu":
+    case 'contextmenu':
       this.isTap = false;
-      let event = this.frame.document.createEvent("MouseEvents");
+      let event = this.frame.document.createEvent('MouseEvents');
       if (this.mouseEventsOnly) {
         target = doc.elementFromPoint(this.lastCoordinates[0], this.lastCoordinates[1]);
-      } else {
+      }
+      else {
         target = this.touchIds[touchId].target;
       }
-
-      let [clientX, clientY, pageX, pageY, screenX, screenY] =
-          this.getCoordinateInfo(target, x, y);
-
-      event.initMouseEvent(
-          "contextmenu",
-          true,
-          true,
-          target.ownerDocument.defaultView,
-          1,
-          screenX,
-          screenY,
-          clientX,
-          clientY,
-          false,
-          false,
-          false,
-          false,
-          0,
-          null);
+      let [ clientX, clientY,
+            pageX, pageY,
+            screenX, screenY ] = this.getCoordinateInfo(target, x, y);
+      event.initMouseEvent('contextmenu', true, true,
+                           target.ownerDocument.defaultView, 1,
+                           screenX, screenY, clientX, clientY,
+                           false, false, false, false, 0, null);
       target.dispatchEvent(event);
       break;
+    default:
+      throw {message:"Unknown event type: " + type, code: 500, stack:null};
+    }
+    this.checkForInterrupted();
+  },
 
-    default:
-      throw new WebDriverError("Unknown event type: " + type);
-  }
-  this.checkForInterrupted();
-};
-
-ActionChain.prototype.mouseTap = function(doc, x, y, button, count, mod) {
-  this.emitMouseEvent(doc, "mousemove", x, y, button, count, mod);
-  this.emitMouseEvent(doc, "mousedown", x, y, button, count, mod);
-  this.emitMouseEvent(doc, "mouseup", x, y, button, count, mod);
-};
+  mouseTap: function (doc, x, y, button, clickCount, keyModifiers) {
+    this.emitMouseEvent(doc, 'mousemove', x, y, button, clickCount, keyModifiers);
+    this.emitMouseEvent(doc, 'mousedown', x, y, button, clickCount, keyModifiers);
+    this.emitMouseEvent(doc, 'mouseup', x, y, button, clickCount, keyModifiers);
+  },
+}
rename from testing/marionette/common.js
rename to testing/marionette/marionette-common.js
--- a/testing/marionette/common.js
+++ b/testing/marionette/marionette-common.js
@@ -1,14 +1,18 @@
 /* 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/. */
 
-// This file contains common code that is shared between
-// driver.jj and listener.js.
+/**
+ *
+ * This file contains common code that is shared between marionette-server.js
+ * and marionette-listener.js.
+ *
+ */
 
 /**
  * Creates an error message for a JavaScript exception thrown during
  * execute_(async_)script.
  *
  * This will generate a [msg, trace] pair like:
  *
  * ['ReferenceError: foo is not defined',
@@ -68,17 +72,17 @@ MarionetteLogObj.prototype = {
    * @param msgs Object
    *        Takes a list of strings
    */
   addLogs: function ML_addLogs(msgs) {
     for (let i = 0; i < msgs.length; i++) {
       this.logs.push(msgs[i]);
     }
   },
-
+  
   /**
    * Return all logged messages.
    */
   getLogs: function ML_getLogs() {
     let logs = this.logs;
     this.clearLogs();
     return logs;
   },
rename from testing/marionette/elements.js
rename to testing/marionette/marionette-elements.js
--- a/testing/marionette/elements.js
+++ b/testing/marionette/marionette-elements.js
@@ -422,28 +422,28 @@ ElementManager.prototype = {
    * @param object values
    *        The 'using' member of values will tell us which search
    *        method to use. The 'value' member tells us the value we
    *        are looking for.
    *        If this object has an 'element' member, this will be used
    *        as the start node instead of the document root
    *        If this object has a 'time' member, this number will be
    *        used to see if we have hit the search timelimit.
+   * @param function on_success
+   *        The notification callback used when we are returning successfully.
+   * @param function on_error
+            The callback to invoke when an error occurs.
    * @param boolean all
    *        If true, all found elements will be returned.
    *        If false, only the first element will be returned.
-   * @param function on_success
-   *        Callback used when operating is successful.
-   * @param function on_error
-   *        Callback to invoke when an error occurs.
    *
    * @return nsIDOMElement or list of nsIDOMElements
    *        Returns the element(s) by calling the on_success function.
    */
-  find: function EM_find(win, values, searchTimeout, all, on_success, on_error, command_id) {
+  find: function EM_find(win, values, searchTimeout, on_success, on_error, all, command_id) {
     let startTime = values.time ? values.time : new Date().getTime();
     let startNode = (values.element != undefined) ?
                     this.getKnownElement(values.element, win) : win.document;
     if (this.elementStrategies.indexOf(values.using) < 0) {
       throw new ElementException("No such strategy.", 32, null);
     }
     let found = all ? this.findElements(values.using, values.value, win.document, startNode) :
                       this.findElement(values.using, values.value, win.document, startNode);
@@ -456,38 +456,39 @@ ElementManager.prototype = {
         } else {
           // Format message depending on strategy if necessary
           let message = "Unable to locate element: " + values.value;
           if (values.using == ANON) {
             message = "Unable to locate anonymous children";
           } else if (values.using == ANON_ATTRIBUTE) {
             message = "Unable to locate anonymous element: " + JSON.stringify(values.value);
           }
-          on_error({message: message, code: 7}, command_id);
+          on_error(message, 7, null, command_id);
         }
       } else {
         values.time = startTime;
         this.timer.initWithCallback(this.find.bind(this, win, values,
-                                                   searchTimeout, all,
-                                                   on_success, on_error,
+                                                   searchTimeout,
+                                                   on_success, on_error, all,
                                                    command_id),
                                     100,
                                     Components.interfaces.nsITimer.TYPE_ONE_SHOT);
       }
     } else {
       if (isArrayLike) {
         let ids = []
         for (let i = 0 ; i < found.length ; i++) {
-          ids.push({"ELEMENT": this.addToKnownElements(found[i])});
+          ids.push({'ELEMENT': this.addToKnownElements(found[i])});
         }
         on_success(ids, command_id);
       } else {
         let id = this.addToKnownElements(found);
-        on_success({"ELEMENT": id}, command_id);
+        on_success({'ELEMENT':id}, command_id);
       }
+      return;
     }
   },
 
   /**
    * Find a value by XPATH
    *
    * @param nsIDOMElement root
    *        Document root
rename from testing/marionette/frame-manager.js
rename to testing/marionette/marionette-frame-manager.js
--- a/testing/marionette/frame-manager.js
+++ b/testing/marionette/marionette-frame-manager.js
@@ -1,21 +1,23 @@
 /* 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/. */
 
-let {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+this.EXPORTED_SYMBOLS = [
+  "FrameManager"
+];
 
-this.EXPORTED_SYMBOLS = ["FrameManager"];
-
-let FRAME_SCRIPT = "chrome://marionette/content/listener.js";
-
+let FRAME_SCRIPT = "chrome://marionette/content/marionette-listener.js";
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+Cu.import("resource://gre/modules/Log.jsm");
+let logger = Log.repository.getLogger("Marionette");
+
 let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                .getService(Ci.mozIJSSubScriptLoader);
 let specialpowers = {};
 
 //list of OOP frames that has the frame script loaded
 let remoteFrames = [];
 
 /**
@@ -92,85 +94,70 @@ FrameManager.prototype = {
         return {value: isLocal};
       case "MarionetteFrame:getCurrentFrameId":
         if (this.currentRemoteFrame != null) {
           return this.currentRemoteFrame.frameId;
         }
     }
   },
 
-  getOopFrame: function FM_getOopFrame(winId, frameId) {
-    // get original frame window
-    let outerWin = Services.wm.getOuterWindowWithId(winId);
-    // find the OOP frame
-    let f = outerWin.document.getElementsByTagName("iframe")[frameId];
-    return f;
-  },
-
-  getFrameMM: function FM_getFrameMM(winId, frameId) {
-    let oopFrame = this.getOopFrame(winId, frameId);
-    let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner)
-        .frameLoader.messageManager;
-    return mm;
-  },
-
-  /**
-   * Switch to OOP frame.  We're handling this here
-   * so we can maintain a list of remote frames.
-   */
-  switchToFrame: function FM_switchToFrame(winId, frameId) {
-    let oopFrame = this.getOopFrame(winId, frameId);
-    let mm = this.getFrameMM(winId, frameId);
+  //This is just 'switch to OOP frame'. We're handling this here so we can maintain a list of remoteFrames.
+  switchToFrame: function FM_switchToFrame(message) {
+    // Switch to a remote frame.
+    let frameWindow = Services.wm.getOuterWindowWithId(message.json.win); //get the original frame window
+    let oopFrame = frameWindow.document.getElementsByTagName("iframe")[message.json.frame]; //find the OOP frame
+    let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager; //get the OOP frame's mm
 
     if (!specialpowers.hasOwnProperty("specialPowersObserver")) {
       loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.js",
-          specialpowers);
+                           specialpowers);
     }
 
-    // See if this frame already has our frame script loaded in it;
-    // if so, just wake it up.
+    // See if this frame already has our frame script loaded in it; if so,
+    // just wake it up.
     for (let i = 0; i < remoteFrames.length; i++) {
       let frame = remoteFrames[i];
       let frameMessageManager = frame.messageManager.get();
+      logger.info("trying remote frame " + i);
       try {
         frameMessageManager.sendAsyncMessage("aliveCheck", {});
-      } catch (e) {
+      }
+      catch(e) {
         if (e.result ==  Components.results.NS_ERROR_NOT_INITIALIZED) {
+          logger.info("deleting frame");
           remoteFrames.splice(i, 1);
           continue;
         }
       }
       if (frameMessageManager == mm) {
         this.currentRemoteFrame = frame;
         this.addMessageManagerListeners(mm);
-
         if (!frame.specialPowersObserver) {
           frame.specialPowersObserver = new specialpowers.SpecialPowersObserver();
           frame.specialPowersObserver.init(mm);
         }
 
-        mm.sendAsyncMessage("Marionette:restart");
+        mm.sendAsyncMessage("Marionette:restart", {});
         return oopFrame.id;
       }
     }
 
     // If we get here, then we need to load the frame script in this frame,
-    // and set the frame's ChromeMessageSender as the active message manager
-    // the server will listen to.
+    // and set the frame's ChromeMessageSender as the active message manager the server will listen to
     this.addMessageManagerListeners(mm);
-    let aFrame = new MarionetteRemoteFrame(winId, frameId);
+    let aFrame = new MarionetteRemoteFrame(message.json.win, message.json.frame);
     aFrame.messageManager = Cu.getWeakReference(mm);
     remoteFrames.push(aFrame);
     this.currentRemoteFrame = aFrame;
 
+    logger.info("frame-manager load script: " + mm.toString());
     mm.loadFrameScript(FRAME_SCRIPT, true, true);
 
     aFrame.specialPowersObserver = new specialpowers.SpecialPowersObserver();
     aFrame.specialPowersObserver.init(mm);
-
     return oopFrame.id;
   },
 
   /*
    * This function handles switching back to the frame that was interrupted by the modal dialog.
    * This function gets called by the interrupted frame once the dialog is dismissed and the frame resumes its process
    */
   switchToModalOrigin: function FM_switchToModalOrigin() {
@@ -192,63 +179,69 @@ FrameManager.prototype = {
       if (frame.specialPowersObserver) {
         frame.specialPowersObserver.uninit();
         frame.specialPowersObserver = null;
       }
     }
   },
 
   /**
-   * Adds message listeners to the server, 
-   * listening for messages from content frame scripts.
-   * It also adds a MarionetteFrame:getInterruptedState
-   * message listener to the FrameManager,
-   * so the frame manager's state can be checked by the frame.
+   * Adds message listeners to the server, listening for messages from content frame scripts.
+   * It also adds a "MarionetteFrame:getInterruptedState" message listener to the FrameManager,
+   * so the frame manager's state can be checked by the frame
    *
-   * @param {nsIMessageListenerManager} mm
-   *     The message manager object, typically
-   *     ChromeMessageBroadcaster or ChromeMessageSender.
+   * @param object messageManager
+   *        The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
+   *        to which the listeners should be added.
    */
-  addMessageManagerListeners: function FM_addMessageManagerListeners(mm) {
-    mm.addWeakMessageListener("Marionette:emitTouchEvent", this.server);
-    mm.addWeakMessageListener("Marionette:log", this.server);
-    mm.addWeakMessageListener("Marionette:runEmulatorCmd", this.server);
-    mm.addWeakMessageListener("Marionette:runEmulatorShell", this.server);
-    mm.addWeakMessageListener("Marionette:shareData", this.server);
-    mm.addWeakMessageListener("Marionette:switchToModalOrigin", this.server);
-    mm.addWeakMessageListener("Marionette:switchedToFrame", this.server);
-    mm.addWeakMessageListener("Marionette:addCookie", this.server);
-    mm.addWeakMessageListener("Marionette:getVisibleCookies", this.server);
-    mm.addWeakMessageListener("Marionette:deleteCookie", this.server);
-    mm.addWeakMessageListener("Marionette:register", this.server);
-    mm.addWeakMessageListener("Marionette:listenersAttached", this.server);
-    mm.addWeakMessageListener("MarionetteFrame:handleModal", this);
-    mm.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
-    mm.addWeakMessageListener("MarionetteFrame:getInterruptedState", this);
+  addMessageManagerListeners: function MDA_addMessageManagerListeners(messageManager) {
+    messageManager.addWeakMessageListener("Marionette:ok", this.server);
+    messageManager.addWeakMessageListener("Marionette:done", this.server);
+    messageManager.addWeakMessageListener("Marionette:error", this.server);
+    messageManager.addWeakMessageListener("Marionette:emitTouchEvent", this.server);
+    messageManager.addWeakMessageListener("Marionette:log", this.server);
+    messageManager.addWeakMessageListener("Marionette:register", this.server);
+    messageManager.addWeakMessageListener("Marionette:runEmulatorCmd", this.server);
+    messageManager.addWeakMessageListener("Marionette:runEmulatorShell", this.server);
+    messageManager.addWeakMessageListener("Marionette:shareData", this.server);
+    messageManager.addWeakMessageListener("Marionette:switchToModalOrigin", this.server);
+    messageManager.addWeakMessageListener("Marionette:switchToFrame", this.server);
+    messageManager.addWeakMessageListener("Marionette:switchedToFrame", this.server);
+    messageManager.addWeakMessageListener("Marionette:addCookie", this.server);
+    messageManager.addWeakMessageListener("Marionette:getVisibleCookies", this.server);
+    messageManager.addWeakMessageListener("Marionette:deleteCookie", this.server);
+    messageManager.addWeakMessageListener("Marionette:listenersAttached", this.server);
+    messageManager.addWeakMessageListener("MarionetteFrame:handleModal", this);
+    messageManager.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
+    messageManager.addWeakMessageListener("MarionetteFrame:getInterruptedState", this);
   },
 
   /**
    * Removes listeners for messages from content frame scripts.
-   * We do not remove the MarionetteFrame:getInterruptedState
-   * or the Marionette:switchToModalOrigin message listener,
-   * because we want to allow all known frames to contact the frame manager
-   * so that it can check if it was interrupted, and if so,
-   * it will call switchToModalOrigin when its process gets resumed.
+   * We do not remove the "MarionetteFrame:getInterruptedState" or the
+   * "Marioentte:switchToModalOrigin" message listener,
+   * because we want to allow all known frames to contact the frame manager so that
+   * it can check if it was interrupted, and if so, it will call switchToModalOrigin
+   * when its process gets resumed.
    *
-   * @param {nsIMessageListenerManager} mm
-   *     The message manager object, typically
-   *     ChromeMessageBroadcaster or ChromeMessageSender.
+   * @param object messageManager
+   *        The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
+   *        from which the listeners should be removed.
    */
-  removeMessageManagerListeners: function FM_removeMessageManagerListeners(mm) {
-    mm.removeWeakMessageListener("Marionette:log", this.server);
-    mm.removeWeakMessageListener("Marionette:shareData", this.server);
-    mm.removeWeakMessageListener("Marionette:runEmulatorCmd", this.server);
-    mm.removeWeakMessageListener("Marionette:runEmulatorShell", this.server);
-    mm.removeWeakMessageListener("Marionette:switchedToFrame", this.server);
-    mm.removeWeakMessageListener("Marionette:addCookie", this.server);
-    mm.removeWeakMessageListener("Marionette:getVisibleCookies", this.server);
-    mm.removeWeakMessageListener("Marionette:deleteCookie", this.server);
-    mm.removeWeakMessageListener("Marionette:listenersAttached", this.server);
-    mm.removeWeakMessageListener("Marionette:register", this.server);
-    mm.removeWeakMessageListener("MarionetteFrame:handleModal", this);
-    mm.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
-  }
+  removeMessageManagerListeners: function MDA_removeMessageManagerListeners(messageManager) {
+    messageManager.removeWeakMessageListener("Marionette:ok", this.server);
+    messageManager.removeWeakMessageListener("Marionette:done", this.server);
+    messageManager.removeWeakMessageListener("Marionette:error", this.server);
+    messageManager.removeWeakMessageListener("Marionette:log", this.server);
+    messageManager.removeWeakMessageListener("Marionette:shareData", this.server);
+    messageManager.removeWeakMessageListener("Marionette:register", this.server);
+    messageManager.removeWeakMessageListener("Marionette:runEmulatorCmd", this.server);
+    messageManager.removeWeakMessageListener("Marionette:runEmulatorShell", this.server);
+    messageManager.removeWeakMessageListener("Marionette:switchToFrame", this.server);
+    messageManager.removeWeakMessageListener("Marionette:switchedToFrame", this.server);
+    messageManager.removeWeakMessageListener("Marionette:addCookie", this.server);
+    messageManager.removeWeakMessageListener("Marionette:getVisibleCookies", this.server);
+    messageManager.removeWeakMessageListener("Marionette:deleteCookie", this.server);
+    messageManager.removeWeakMessageListener("Marionette:listenersAttached", this.server);
+    messageManager.removeWeakMessageListener("MarionetteFrame:handleModal", this);
+    messageManager.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
+  },
 };
rename from testing/marionette/listener.js
rename to testing/marionette/marionette-listener.js
--- a/testing/marionette/listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -6,30 +6,30 @@
 let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 let uuidGen = Cc["@mozilla.org/uuid-generator;1"]
                 .getService(Ci.nsIUUIDGenerator);
 
 let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                .getService(Ci.mozIJSSubScriptLoader);
 
-loader.loadSubScript("chrome://marionette/content/simpletest.js");
-loader.loadSubScript("chrome://marionette/content/common.js");
-loader.loadSubScript("chrome://marionette/content/actions.js");
-Cu.import("chrome://marionette/content/elements.js");
+loader.loadSubScript("chrome://marionette/content/marionette-simpletest.js");
+loader.loadSubScript("chrome://marionette/content/marionette-common.js");
+loader.loadSubScript("chrome://marionette/content/marionette-actions.js");
+Cu.import("chrome://marionette/content/marionette-elements.js");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 let utils = {};
 utils.window = content;
 // Load Event/ChromeUtils for use with JS scripts:
 loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
 loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils);
 loader.loadSubScript("chrome://marionette/content/atoms.js", utils);
-loader.loadSubScript("chrome://marionette/content/sendkeys.js", utils);
+loader.loadSubScript("chrome://marionette/content/marionette-sendkeys.js", utils);
 
 loader.loadSubScript("chrome://specialpowers/content/specialpowersAPI.js");
 loader.loadSubScript("chrome://specialpowers/content/specialpowers.js");
 
 let marionetteLogObj = new MarionetteLogObj();
 
 let isB2G = false;
 
@@ -69,17 +69,17 @@ let navTimer = Cc["@mozilla.org/timer;1"
 let onDOMContentLoaded;
 // Send move events about this often
 let EVENT_INTERVAL = 30; // milliseconds
 // last touch for each fingerId
 let multiLast = {};
 
 Cu.import("resource://gre/modules/Log.jsm");
 let logger = Log.repository.getLogger("Marionette");
-logger.info("loaded listener.js");
+logger.info("loaded marionette-listener.js");
 let modalHandler = function() {
   // This gets called on the system app only since it receives the mozbrowserprompt event
   sendSyncMessage("Marionette:switchedToFrame", { frameValue: null, storePrevious: true });
   let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value;
   if (isLocal) {
     previousFrame = curFrame;
   }
   curFrame = content;
@@ -102,21 +102,19 @@ function registerSelf() {
     if (typeof id != "undefined") {
       // check if we're the main process
       if (register[0][1] == true) {
         addMessageListener("MarionetteMainListener:emitTouchEvent", emitTouchEventForIFrame);
       }
       importedScripts = FileUtils.getDir('TmpD', [], false);
       importedScripts.append('marionetteContentScripts');
       startListeners();
-      let rv = {};
       if (remotenessChange) {
-        rv.listenerId = id;
+        sendAsyncMessage("Marionette:listenersAttached", {listenerId: id});
       }
-      sendAsyncMessage("Marionette:listenersAttached", rv);
     }
   }
 }
 
 function emitTouchEventForIFrame(message) {
   message = message.json;
   let identifier = actions.nextTouchId;
 
@@ -356,19 +354,19 @@ function sendOk(command_id) {
  */
 function sendLog(msg) {
   sendToServer("Marionette:log", { message: msg });
 }
 
 /**
  * Send error message to server
  */
-function sendError(msg, code, stack, cmdId) {
-  let payload = {message: msg, code: code, stack: stack};
-  sendToServer("Marionette:error", payload, cmdId);
+function sendError(message, status, trace, command_id) {
+  let error_msg = { message: message, status: status, stacktrace: trace };
+  sendToServer("Marionette:error", error_msg, command_id);
 }
 
 /**
  * Clear test values after completion of test
  */
 function resetValues() {
   sandbox = null;
   curFrame = content;
@@ -429,18 +427,16 @@ function createExecuteContentSandbox(aWi
   sandbox.navigator = sandbox.window.navigator;
   sandbox.testUtils = utils;
   sandbox.asyncTestCommandId = asyncTestCommandId;
 
   let marionette = new Marionette(this, aWindow, "content",
                                   marionetteLogObj, timeout,
                                   heartbeatCallback,
                                   marionetteTestName);
-  marionette.runEmulatorCmd = (cmd, cb) => this.runEmulatorCmd(cmd, cb);
-  marionette.runEmulatorShell = (args, cb) => this.runEmulatorShell(args, cb);
   sandbox.marionette = marionette;
   marionette.exports.forEach(function(fn) {
     try {
       sandbox[fn] = marionette[fn].bind(marionette);
     }
     catch(e) {
       sandbox[fn] = marionette[fn];
     }
@@ -964,27 +960,18 @@ function actionChain(msg) {
   callbacks.onError = (message, code, trace) => {
     sendError(message, code, trace, msg.json.command_id);
   };
 
   let touchProvider = {};
   touchProvider.createATouch = createATouch;
   touchProvider.emitTouchEvent = emitTouchEvent;
 
-  try {
-    actions.dispatchActions(
-        args,
-        touchId,
-        curFrame,
-        elementManager,
-        callbacks,
-        touchProvider);
-  } catch (e) {
-    sendError(e.message, e.code, e.stack, command_id);
-  }
+  actions.dispatchActions(args, touchId, curFrame, elementManager, callbacks,
+                          touchProvider);
 }
 
 /**
  * Function to emit touch events which allow multi touch on the screen
  * @param type represents the type of event, touch represents the current touch,touches are all pending touches
  */
 function emitMultiEvents(type, touch, touches) {
   let target = touch.target;
@@ -1300,36 +1287,36 @@ function refresh(msg) {
 }
 
 /**
  * Find an element in the document using requested search strategy
  */
 function findElementContent(msg) {
   let command_id = msg.json.command_id;
   try {
-    let on_success = function(el, cmd_id) { sendResponse({value: el}, cmd_id) };
-    let on_error = function(e, cmd_id) { sendError(e.message, e.code, null, cmd_id); };
+    let on_success = function(id, cmd_id) { sendResponse({value:id}, cmd_id); };
+    let on_error = sendError;
     elementManager.find(curFrame, msg.json, msg.json.searchTimeout,
-                        false /* all */, on_success, on_error, command_id);
+                        on_success, on_error, false, command_id);
   }
   catch (e) {
     sendError(e.message, e.code, e.stack, command_id);
   }
 }
 
 /**
  * Find elements in the document using requested search strategy
  */
 function findElementsContent(msg) {
   let command_id = msg.json.command_id;
   try {
-    let on_success = function(els, cmd_id) { sendResponse({value: els}, cmd_id); };
-    let on_error = function(e, cmd_id) { sendError(e.message, e.code, null, cmd_id); };
+    let on_success = function(id, cmd_id) { sendResponse({value:id}, cmd_id); };
+    let on_error = sendError;
     elementManager.find(curFrame, msg.json, msg.json.searchTimeout,
-                        true /* all */, on_success, on_error, command_id);
+                        on_success, on_error, true, command_id);
   }
   catch (e) {
     sendError(e.message, e.code, e.stack, command_id);
   }
 }
 
 /**
  * Find and return the active element on the page
@@ -1710,30 +1697,31 @@ function switchToFrame(msg) {
 
   sandbox = null;
 
   // send a synchronous message to let the server update the currently active
   // frame element (for getActiveFrame)
   let frameValue = elementManager.wrapValue(curFrame.wrappedJSObject)['ELEMENT'];
   sendSyncMessage("Marionette:switchedToFrame", { frameValue: frameValue });
 
-  let rv = null;
-  if (curFrame.contentWindow === null) {
-    // The frame we want to switch to is a remote/OOP frame;
-    // notify our parent to handle the switch
+  if (curFrame.contentWindow == null) {
+    // The frame we want to switch to is a remote (out-of-process) frame;
+    // notify our parent to handle the switch.
     curFrame = content;
-    rv = {win: parWindow, frame: foundFrame};
-  } else {
+    sendToServer('Marionette:switchToFrame', {win: parWindow,
+                                              frame: foundFrame,
+                                              command_id: command_id});
+  }
+  else {
     curFrame = curFrame.contentWindow;
-    if (msg.json.focus)
+    if(msg.json.focus == true) {
       curFrame.focus();
+    }
     checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
   }
-
-  sendResponse({value: rv}, command_id);
 }
  /**
   * Add a cookie to the document
   */
 function addCookie(msg) {
   let cookie = msg.json.cookie;
   if (!cookie.expiry) {
     var date = new Date();
rename from testing/marionette/sendkeys.js
rename to testing/marionette/marionette-sendkeys.js
--- a/testing/marionette/sendkeys.js
+++ b/testing/marionette/marionette-sendkeys.js
@@ -118,18 +118,18 @@ function sendSingleKey (keyToSend, modif
     let modName = keyModifierNames[keyCode];
     modifiers[modName] = !modifiers[modName];
   } else if (modifiers.shiftKey) {
     keyCode = keyCode.toUpperCase();
   }
   utils.synthesizeKey(keyCode, modifiers, document);
 }
 
-function sendKeysToElement (document, element, keysToSend, successCallback, errorCallback, command_id, ignoreVisibility) {
-  if (ignoreVisibility || checkVisible(element)) {
+function sendKeysToElement (document, element, keysToSend, successCallback, errorCallback, command_id, context) {
+  if (context == "chrome" || checkVisible(element)) {
     element.focus();
     let modifiers = {
       shiftKey: false,
       ctrlKey: false,
       altKey: false,
       metaKey: false
     };
     let value = keysToSend.join("");
new file mode 100644
--- /dev/null
+++ b/testing/marionette/marionette-server.js
@@ -0,0 +1,3680 @@
+/* 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";
+
+const FRAME_SCRIPT = "chrome://marionette/content/marionette-listener.js";
+const BROWSER_STARTUP_FINISHED = "browser-delayed-startup-finished";
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+// import logger
+Cu.import("resource://gre/modules/Log.jsm");
+let logger = Log.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-common.js");
+loader.loadSubScript("chrome://marionette/content/marionette-actions.js");
+Cu.import("resource://gre/modules/Services.jsm");
+loader.loadSubScript("chrome://marionette/content/marionette-frame-manager.js");
+Cu.import("chrome://marionette/content/marionette-elements.js");
+let utils = {};
+loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
+loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils);
+loader.loadSubScript("chrome://marionette/content/atoms.js", utils);
+loader.loadSubScript("chrome://marionette/content/marionette-sendkeys.js", utils);
+
+let specialpowers = {};
+
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+function isMulet() {
+  let isMulet = false;
+  try {
+   isMulet = Services.prefs.getBoolPref("b2g.is_mulet");
+  } catch (ex) { }
+  return isMulet;
+}
+
+Services.prefs.setBoolPref("marionette.contentListener", false);
+let appName = isMulet() ? "B2G" : Services.appinfo.name;
+
+let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
+this.DevToolsUtils = DevToolsUtils;
+loader.loadSubScript("resource://gre/modules/devtools/transport/transport.js");
+
+let bypassOffline = false;
+let qemu = "0";
+let device = null;
+const SECURITY_PREF = 'security.turn_off_all_security_so_that_viruses_can_take_over_this_computer';
+
+XPCOMUtils.defineLazyServiceGetter(this, "cookieManager",
+                                   "@mozilla.org/cookiemanager;1",
+                                   "nsICookieManager");
+
+try {
+  XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
+    Cu.import("resource://gre/modules/systemlibs.js");
+    return libcutils;
+  });
+  if (libcutils) {
+    qemu = libcutils.property_get("ro.kernel.qemu");
+    logger.info("B2G emulator: " + (qemu == "1" ? "yes" : "no"));
+    device = libcutils.property_get("ro.product.device");
+    logger.info("Device detected is " + device);
+    bypassOffline = (qemu == "1" || device == "panda");
+  }
+}
+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-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);
+
+// This is used on desktop to prevent newSession from returning before a page
+// load initiated by the Firefox command line has completed.
+let delayedBrowserStarted = false;
+Services.obs.addObserver(function () {
+  delayedBrowserStarted = true;
+}, BROWSER_STARTUP_FINISHED, false);
+
+/*
+ * Custom exceptions
+ */
+function FrameSendNotInitializedError(frame) {
+  this.code = 54;
+  this.frame = frame;
+  this.message = "Error sending message to frame (NS_ERROR_NOT_INITIALIZED)";
+  this.toString = function() {
+    return this.message + " " + this.frame + "; frame has closed.";
+  }
+}
+
+function FrameSendFailureError(frame) {
+  this.code = 55;
+  this.frame = frame;
+  this.message = "Error sending message to frame (NS_ERROR_FAILURE)";
+  this.toString = function() {
+    return this.message + " " + this.frame + "; frame not responding.";
+  }
+}
+
+/**
+ * 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 MarionetteServerConnection(aPrefix, aTransport, aServer)
+{
+  this.uuidGen = Cc["@mozilla.org/uuid-generator;1"]
+                   .getService(Ci.nsIUUIDGenerator);
+
+  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.sessionId = null;
+
+  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;
+  this.pageTimeout = null;
+  this.timer = null;
+  this.inactivityTimer = null;
+  this.heartbeatCallback = function () {}; // called by simpletest methods
+  this.marionetteLog = new MarionetteLogObj();
+  this.command_id = null;
+  this.mainFrame = null; //topmost chrome frame
+  this.curFrame = null; // chrome iframe that currently has focus
+  this.mainContentFrameId = null;
+  this.importedScripts = FileUtils.getFile('TmpD', ['marionetteChromeScripts']);
+  this.importedScriptHashes = {"chrome" : [], "content": []};
+  this.currentFrameElement = null;
+  this.testName = null;
+  this.mozBrowserClose = null;
+  this.enabled_security_pref = false;
+  this.sandbox = null;
+  this.oopFrameId = null; // frame ID of current remote frame, used for mozbrowserclose events
+  this.sessionCapabilities = {
+    // Mandated capabilities
+    "browserName": appName,
+    "browserVersion": Services.appinfo.version,
+    "platformName": Services.appinfo.OS.toUpperCase(),
+    "platformVersion": Services.appinfo.platformVersion,
+
+    // Supported features
+    "handlesAlerts": false,
+    "nativeEvents": false,
+    "raisesAccessibilityExceptions": false,
+    "rotatable": appName == "B2G",
+    "secureSsl": false,
+    "takesElementScreenshot": true,
+    "takesScreenshot": true,
+
+    // Selenium 2 compat
+    "platform": Services.appinfo.OS.toUpperCase(),
+
+    // Proprietary extensions
+    "XULappId" : Services.appinfo.ID,
+    "appBuildId" : Services.appinfo.appBuildID,
+    "device": qemu == "1" ? "qemu" : (!device ? "desktop" : device),
+    "version": Services.appinfo.version
+  };
+
+  this.observing = null;
+  this._browserIds = new WeakMap();
+  this.quitFlags = null;
+  this.actions = new ActionChain(utils);
+}
+
+MarionetteServerConnection.prototype = {
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
+                                         Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference]),
+
+  /**
+   * Debugger transport callbacks:
+   */
+  onPacket: function MSC_onPacket(aPacket) {
+    // Dispatch the request
+    if (this.requestTypes && this.requestTypes[aPacket.name]) {
+      try {
+        this.logRequest(aPacket.name, aPacket);
+        this.requestTypes[aPacket.name].bind(this)(aPacket);
+      } catch(e) {
+        this.conn.send({from:this.actorID, error: {
+                                                    message: ("error occurred while processing '" +
+                                                              aPacket.name),
+                                                    status: 500,
+                                                    stacktrace: e.message }});
+      }
+    } else {
+      this.conn.send({from:this.actorID, error: {
+                                                  message: "unrecognizedPacketType",
+                                                  status: 500,
+                                                  stacktrace: ('Marionette does not ' +
+                                                               'recognize the packet type "' +
+                                                                aPacket.name + '"')
+                                                }
+                      });
+    }
+  },
+
+  onClosed: function MSC_onClosed(aStatus) {
+    this.server._connectionClosed(this);
+    this.sessionTearDown();
+
+    if (this.quitFlags !== null) {
+      let flags = this.quitFlags;
+      this.quitFlags = null;
+      Services.startup.quit(flags);
+    }
+  },
+
+  /**
+   * Helper methods:
+   */
+
+  /**
+   * Switches to the global ChromeMessageBroadcaster, potentially replacing a frame-specific
+   * ChromeMessageSender.  Has no effect if the global ChromeMessageBroadcaster is already
+   * in use.  If this replaces a frame-specific ChromeMessageSender, it removes the message
+   * listeners from that sender, and then puts the corresponding frame script "to sleep",
+   * which removes most of the message listeners from it as well.
+   */
+  switchToGlobalMessageManager: function MDA_switchToGlobalMM() {
+    if (this.curBrowser && this.curBrowser.frameManager.currentRemoteFrame !== null) {
+      this.curBrowser.frameManager.removeMessageManagerListeners(this.messageManager);
+      this.sendAsync("sleepSession", null, null, true);
+      this.curBrowser.frameManager.currentRemoteFrame = null;
+    }
+    this.messageManager = this.globalMessageManager;
+  },
+
+  /**
+   * Helper method to send async messages to the content listener
+   *
+   * @param string name
+   *        Suffix of the targetted message listener (Marionette:<suffix>)
+   * @param object values
+   *        Object to send to the listener
+   */
+  sendAsync: function MDA_sendAsync(name, values, commandId, ignoreFailure) {
+    let success = true;
+    if (commandId) {
+      values.command_id = commandId;
+    }
+    if (this.curBrowser.frameManager.currentRemoteFrame !== null) {
+      try {
+        this.messageManager.sendAsyncMessage(
+          "Marionette:" + name + this.curBrowser.frameManager.currentRemoteFrame.targetFrameId, values);
+      }
+      catch(e) {
+        if (!ignoreFailure) {
+          success = false;
+          let error = e;
+          switch(e.result) {
+            case Components.results.NS_ERROR_FAILURE:
+              error = new FrameSendFailureError(this.curBrowser.frameManager.currentRemoteFrame);
+              break;
+            case Components.results.NS_ERROR_NOT_INITIALIZED:
+              error = new FrameSendNotInitializedError(this.curBrowser.frameManager.currentRemoteFrame);
+              break;
+            default:
+              break;
+          }
+          let code = error.hasOwnProperty('code') ? e.code : 500;
+          this.sendError(error.toString(), code, error.stack, commandId);
+        }
+      }
+    }
+    else {
+      this.curBrowser.executeWhenReady(() => {
+        this.messageManager.broadcastAsyncMessage(
+          "Marionette:" + name + this.curBrowser.curFrameId, values);
+      });
+    }
+    return success;
+  },
+
+  logRequest: function MDA_logRequest(type, data) {
+    logger.debug("Got request: " + type + ", data: " + JSON.stringify(data) + ", id: " + this.command_id);
+  },
+
+  /**
+   * Generic method to pass a response to the client
+   *
+   * @param object msg
+   *        Response to send back to client
+   * @param string command_id
+   *        Unique identifier assigned to the client's request.
+   *        Used to distinguish the asynchronous responses.
+   */
+  sendToClient: function MDA_sendToClient(msg, command_id) {
+    logger.info("sendToClient: " + JSON.stringify(msg) + ", " + command_id +
+                ", " + this.command_id);
+    if (!command_id) {
+      logger.warn("got a response with no command_id");
+      return;
+    }
+    else if (command_id != -1) {
+      // A command_id of -1 is used for emulator callbacks, and those
+      // don't use this.command_id.
+      if (!this.command_id) {
+        // A null value for this.command_id means we've already processed
+        // a message for the previous value, and so the current message is a
+        // duplicate.
+        logger.warn("ignoring duplicate response for command_id " + command_id);
+        return;
+      }
+      else if (this.command_id != command_id) {
+        logger.warn("ignoring out-of-sync response");
+        return;
+      }
+    }
+
+    if (this.curBrowser !== null) {
+      this.curBrowser.pendingCommands = [];
+    }
+
+    this.conn.send(msg);
+    if (command_id != -1) {
+      // Don't unset this.command_id if this message is to process an
+      // emulator callback, since another response for this command_id is
+      // expected, after the containing call to execute_async_script finishes.
+      this.command_id = null;
+    }
+  },
+
+  /**
+   * Send a value to client
+   *
+   * @param object value
+   *        Value to send back to client
+   * @param string command_id
+   *        Unique identifier assigned to the client's request.
+   *        Used to distinguish the asynchronous responses.
+   */
+  sendResponse: function MDA_sendResponse(value, command_id) {
+    if (typeof(value) == 'undefined')
+        value = null;
+    this.sendToClient({from:this.actorID,
+                       sessionId: this.sessionId,
+                       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) {
+    this.sendToClient({from:this.actorID, ok: true}, command_id);
+  },
+
+  /**
+   * Send error message to client
+   *
+   * @param string message
+   *        Error message
+   * @param number status
+   *        Status number
+   * @param string trace
+   *        Stack trace
+   * @param string command_id
+   *        Unique identifier assigned to the client's request.
+   *        Used to distinguish the asynchronous responses.
+   */
+  sendError: function MDA_sendError(message, status, trace, command_id) {
+    let error_msg = {message: message, status: status, stacktrace: trace};
+    this.sendToClient({from:this.actorID, error: error_msg}, command_id);
+  },
+
+  /**
+   * Gets the current active window
+   *
+   * @return nsIDOMWindow
+   */
+  getCurrentWindow: function MDA_getCurrentWindow() {
+    let type = null;
+    if (this.curFrame == null) {
+      if (this.curBrowser == null) {
+        if (this.context == "content") {
+          type = 'navigator:browser';
+        }
+        return Services.wm.getMostRecentWindow(type);
+      }
+      else {
+        return this.curBrowser.window;
+      }
+    }
+    else {
+      return this.curFrame;
+    }
+  },
+
+  /**
+   * Gets the the window enumerator
+   *
+   * @return nsISimpleEnumerator
+   */
+  getWinEnumerator: function MDA_getWinEnumerator() {
+    let type = null;
+    if (appName != "B2G" && this.context == "content") {
+      type = 'navigator:browser';
+    }
+    return Services.wm.getEnumerator(type);
+  },
+
+  /**
+  */
+  addFrameCloseListener: function MDA_addFrameCloseListener(action) {
+    let curWindow = this.getCurrentWindow();
+    let self = this;
+    this.mozBrowserClose = function(e) {
+      if (e.target.id == self.oopFrameId) {
+        curWindow.removeEventListener('mozbrowserclose', self.mozBrowserClose, true);
+        self.switchToGlobalMessageManager();
+        self.sendError("The frame closed during the " + action +  ", recovering to allow further communications", 55, null, self.command_id);
+      }
+    };
+    curWindow.addEventListener('mozbrowserclose', this.mozBrowserClose, true);
+  },
+
+  /**
+   * Create a new BrowserObj for window and add to known browsers
+   *
+   * @param nsIDOMWindow win
+   *        Window for which we will create a BrowserObj
+   *
+   * @return string
+   *        Returns the unique server-assigned ID of the window
+   */
+  addBrowser: function MDA_addBrowser(win) {
+    let browser = new BrowserObj(win, this);
+    let winId = win.QueryInterface(Ci.nsIInterfaceRequestor).
+                    getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+    winId = winId + ((appName == "B2G") ? '-b2g' : '');
+    this.browsers[winId] = browser;
+    this.curBrowser = this.browsers[winId];
+    if (this.curBrowser.elementManager.seenItems[winId] == undefined) {
+      //add this to seenItems so we can guarantee the user will get winId as this window's id
+      this.curBrowser.elementManager.seenItems[winId] = Cu.getWeakReference(win);
+    }
+  },
+
+  /**
+   * Start a new session in a new browser.
+   *
+   * If newSession is true, we will switch focus to the start frame
+   * when it registers.
+   *
+   * @param nsIDOMWindow win
+   *        Window whose browser we need to access
+   * @param boolean newSession
+   *        True if this is the first time we're talking to this browser
+   */
+  startBrowser: function MDA_startBrowser(win, newSession) {
+    this.mainFrame = win;
+    this.curFrame = null;
+    this.addBrowser(win);
+    this.curBrowser.newSession = newSession;
+    this.curBrowser.startSession(newSession, win, this.whenBrowserStarted.bind(this));
+  },
+
+  /**
+   * Callback invoked after a new session has been started in a browser.
+   * Loads the Marionette frame script into the browser if needed.
+   *
+   * @param nsIDOMWindow win
+   *        Window whose browser we need to access
+   * @param boolean newSession
+   *        True if this is the first time we're talking to this browser
+   */
+  whenBrowserStarted: function MDA_whenBrowserStarted(win, newSession) {
+    utils.window = win;
+
+    try {
+      let mm = win.window.messageManager;
+      if (!newSession) {
+        // Loading the frame script corresponds to a situation we need to
+        // return to the server. If the messageManager is a message broadcaster
+        // with no children, we don't have a hope of coming back from this call,
+        // so send the ack here. Otherwise, make a note of how many child scripts
+        // will be loaded so we known when it's safe to return.
+        if (mm.childCount === 0) {
+          this.sendOk(this.command_id);
+        } else {
+          this.curBrowser.frameRegsPending = mm.childCount;
+        }
+      }
+
+      if (!Services.prefs.getBoolPref("marionette.contentListener") || !newSession) {
+        mm.loadFrameScript(FRAME_SCRIPT, true, true);
+        Services.prefs.setBoolPref("marionette.contentListener", true);
+      }
+    }
+    catch (e) {
+      //there may not always be a content process
+      logger.info("could not load listener into content for page: " + win.location.href);
+    }
+  },
+
+  /**
+   * Recursively get all labeled text
+   *
+   * @param nsIDOMElement el
+   *        The parent element
+   * @param array lines
+   *        Array that holds the text lines
+   */
+  getVisibleText: function MDA_getVisibleText(el, lines) {
+    let nodeName = el.nodeName;
+    try {
+      if (utils.isElementDisplayed(el)) {
+        if (el.value) {
+          lines.push(el.value);
+        }
+        for (var child in el.childNodes) {
+          this.getVisibleText(el.childNodes[child], lines);
+        };
+      }
+    }
+    catch (e) {
+      if (nodeName == "#text") {
+        lines.push(el.textContent);
+      }
+    }
+  },
+
+  getCommandId: function MDA_getCommandId() {
+    return this.uuidGen.generateUUID().toString();
+  },
+
+  /**
+    * Given a file name, this will delete the file from the temp directory if it exists
+    */
+  deleteFile: function(filename) {
+    let file = FileUtils.getFile('TmpD', [filename.toString()]);
+    if (file.exists()) {
+      file.remove(true);
+    }
+  },
+
+  /**
+   * Marionette API:
+   *
+   * All methods implementing a command from the client should create a
+   * command_id, and then use this command_id in all messages exchanged with
+   * the frame scripts and with responses sent to the client.  This prevents
+   * commands and responses from getting out-of-sync, which can happen in
+   * the case of execute_async calls that timeout and then later send a
+   * response, and other situations.  See bug 779011. See setScriptTimeout()
+   * for a basic example.
+   */
+
+  /**
+   * Create a new session. This creates a new BrowserObj.
+   *
+   * This will send a hash map of supported capabilities to the client
+   * as part of the Marionette:register IPC command in the
+   * receiveMessage callback when a new browser is created.
+   */
+  newSession: function MDA_newSession(aRequest) {
+    logger.info("The newSession request is " + JSON.stringify(aRequest))
+    this.command_id = this.getCommandId();
+    this.newSessionCommandId = this.command_id;
+
+    // SpecialPowers requires insecure automation-only features that we put behind a pref
+    let security_pref_value = false;
+    try {
+      security_pref_value = Services.prefs.getBoolPref(SECURITY_PREF);
+    } catch(e) {}
+    if (!security_pref_value) {
+      this.enabled_security_pref = true;
+      Services.prefs.setBoolPref(SECURITY_PREF, true);
+    }
+
+    if (!specialpowers.hasOwnProperty('specialPowersObserver')) {
+      loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.js",
+                           specialpowers);
+      specialpowers.specialPowersObserver = new specialpowers.SpecialPowersObserver();
+      specialpowers.specialPowersObserver.init();
+      specialpowers.specialPowersObserver._loadFrameScript();
+    }
+
+    this.scriptTimeout = 10000;
+    if (aRequest && aRequest.parameters) {
+      this.sessionId = aRequest.parameters.sessionId ? aRequest.parameters.sessionId : aRequest.parameters.session_id;
+      logger.info("Session Id is set to: " + this.sessionId);
+      try {
+        this.setSessionCapabilities(aRequest.parameters.capabilities);
+      } catch (e) {
+        // 71 error is "session not created"
+        this.sendError(e.message + " " + JSON.stringify(e.errors), 71, null,
+                       this.command_id);
+        return;
+      }
+    }
+
+    if (appName == "Firefox") {
+      this._dialogWindowRef = null;
+      let modalHandler = this.handleDialogLoad.bind(this);
+      this.observing = {
+        "tabmodal-dialog-loaded": modalHandler,
+        "common-dialog-loaded": modalHandler
+      }
+      for (let topic in this.observing) {
+        Services.obs.addObserver(this.observing[topic], topic, false);
+      }
+    }
+
+    function waitForWindow() {
+      let win = this.getCurrentWindow();
+      if (!win) {
+        // If the window isn't even created, just poll wait for it
+        let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+        checkTimer.initWithCallback(waitForWindow.bind(this), 100,
+                                    Ci.nsITimer.TYPE_ONE_SHOT);
+      }
+      else if (win.document.readyState != "complete") {
+        // Otherwise, wait for it to be fully loaded before proceeding
+        let listener = (evt) => {
+          // ensure that we proceed, on the top level document load event
+          // (not an iframe one...)
+          if (evt.target != win.document) {
+            return;
+          }
+          win.removeEventListener("load", listener);
+          waitForWindow.call(this);
+        };
+        win.addEventListener("load", listener, true);
+      }
+      else {
+        let clickToStart;
+        try {
+          clickToStart = Services.prefs.getBoolPref('marionette.debugging.clicktostart');
+        } catch (e) { }
+        if (clickToStart && (appName != "B2G")) {
+          let pService = Cc["@mozilla.org/embedcomp/prompt-service;1"]
+                           .getService(Ci.nsIPromptService);
+          pService.alert(win, "", "Click to start execution of marionette tests");
+        }
+        this.startBrowser(win, true);
+      }
+    }
+
+    function runSessionStart() {
+      if (!Services.prefs.getBoolPref("marionette.contentListener")) {
+        waitForWindow.call(this);
+      }
+      else if ((appName != "Firefox") && (this.curBrowser === null)) {
+        // If there is a content listener, then we just wake it up
+        this.addBrowser(this.getCurrentWindow());
+        this.curBrowser.startSession(false, this.getCurrentWindow(),
+                                     this.whenBrowserStarted);
+        this.messageManager.broadcastAsyncMessage("Marionette:restart", {});
+      }
+      else {
+        this.sendError("Session already running", 500, null,
+                       this.command_id);
+      }
+      this.switchToGlobalMessageManager();
+    }
+
+    if (!delayedBrowserStarted && (appName != "B2G")) {
+      let self = this;
+      Services.obs.addObserver(function onStart () {
+        Services.obs.removeObserver(onStart, BROWSER_STARTUP_FINISHED);
+        runSessionStart.call(self);
+      }, BROWSER_STARTUP_FINISHED, false);
+    } else {
+      runSessionStart.call(this);
+    }
+  },
+
+  /**
+   * Send the current session's capabilities to the client.
+   *
+   * Capabilities informs the client of which WebDriver features are
+   * supported by Firefox and Marionette.  They are immutable for the
+   * length of the session.
+   *
+   * The return value is an immutable map of string keys
+   * ("capabilities") to values, which may be of types boolean,
+   * numerical or string.
+   */
+  getSessionCapabilities: function MDA_getSessionCapabilities() {
+    this.command_id = this.getCommandId();
+
+    if (!this.sessionId) {
+      this.sessionId = this.uuidGen.generateUUID().toString();
+    }
+
+    // eideticker (bug 965297) and mochitest (bug 965304)
+    // compatibility.  They only check for the presence of this
+    // property and should so not be in caps if not on a B2G device.
+    if (appName == "B2G")
+      this.sessionCapabilities.b2g = true;
+
+    this.sendResponse(this.sessionCapabilities, this.command_id);
+  },
+
+  /**
+   * Update the sessionCapabilities object with the keys that have been
+   * passed in when a new session is created.
+   *
+   * This part of the WebDriver spec is currently in flux, see
+   * http://lists.w3.org/Archives/Public/public-browser-tools-testing/2014OctDec/0000.html
+   *
+   * This is not a public API, only available when a new session is
+   * created.
+   *
+   * @param Object newCaps key/value dictionary to overwrite
+   *   session's current capabilities
+   */
+  setSessionCapabilities: function(newCaps) {
+    const copy = (from, to={}) => {
+      let errors = {};
+      for (let key in from) {
+        if (key === "desiredCapabilities"){
+          // Keeping desired capabilities separate for now so that we can keep
+          // backwards compatibility
+          to = copy(from[key], to);
+        } else if (key === "requiredCapabilities") {
+          for (let caps in from[key]) {
+            if (from[key][caps] !== this.sessionCapabilities[caps]) {
+              errors[caps] = from[key][caps] + " does not equal " + this.sessionCapabilities[caps]
+            }
+          }
+        }
+        to[key] = from[key];
+      }
+      if (Object.keys(errors).length === 0){
+        return to;
+      }
+      else {
+        throw { "message": "Not all requiredCapabilities could be met",
+                "errors": errors}
+      }
+    };
+
+    // Clone, overwrite, and set.
+    let caps = copy(this.sessionCapabilities);
+    caps = copy(newCaps, caps);
+    this.sessionCapabilities = caps;
+  },
+
+  /**
+   * Log message. Accepts user defined log-level.
+   *
+   * @param object aRequest
+   *        'value' member holds log message
+   *        'level' member hold log level
+   */
+  log: function MDA_log(aRequest) {
+    this.command_id = this.getCommandId();
+    this.marionetteLog.log(aRequest.parameters.value, aRequest.parameters.level);
+    this.sendOk(this.command_id);
+  },
+
+  /**
+   * Return all logged messages.
+   */
+  getLogs: function MDA_getLogs() {
+    this.command_id = this.getCommandId();
+    this.sendResponse(this.marionetteLog.getLogs(), this.command_id);
+  },
+
+  /**
+   * Sets the context of the subsequent commands to be either 'chrome' or 'content'
+   *
+   * @param object aRequest
+   *        'value' member holds the name of the context to be switched to
+   */
+  setContext: function MDA_setContext(aRequest) {
+    this.command_id = this.getCommandId();
+    let context = aRequest.parameters.value;
+    if (context != "content" && context != "chrome") {
+      this.sendError("invalid context", 500, null, this.command_id);
+    }
+    else {
+      this.context = context;
+      this.sendOk(this.command_id);
+    }
+  },
+
+  /**
+   * Gets the context of the server, either 'chrome' or 'content'.
+   */
+  getContext: function MDA_getContext() {
+    this.command_id = this.getCommandId();
+    this.sendResponse(this.context, this.command_id);
+  },
+
+  /**
+   * Returns a chrome sandbox that can be used by the execute_foo functions.
+   *
+   * @param nsIDOMWindow aWindow
+   *        Window in which we will execute code
+   * @param Marionette marionette
+   *        Marionette test instance
+   * @param object args
+   *        Client given args
+   * @return Sandbox
+   *        Returns the sandbox
+   */
+  createExecuteSandbox: function MDA_createExecuteSandbox(aWindow, marionette, specialPowers, command_id) {
+    let _chromeSandbox = new Cu.Sandbox(aWindow,
+       { sandboxPrototype: aWindow, wantXrays: false, sandboxName: ''});
+    _chromeSandbox.global = _chromeSandbox;
+    _chromeSandbox.testUtils = utils;
+
+    marionette.exports.forEach(function(fn) {
+      try {
+        _chromeSandbox[fn] = marionette[fn].bind(marionette);
+      }
+      catch(e) {
+        _chromeSandbox[fn] = marionette[fn];
+      }
+    });
+
+    _chromeSandbox.isSystemMessageListenerReady =
+        function() { return systemMessageListenerReady; }
+
+    if (specialPowers == true) {
+      loader.loadSubScript("chrome://specialpowers/content/specialpowersAPI.js",
+                           _chromeSandbox);
+      loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserverAPI.js",
+                           _chromeSandbox);
+      loader.loadSubScript("chrome://specialpowers/content/ChromePowers.js",
+                           _chromeSandbox);
+    }
+
+    return _chromeSandbox;
+  },
+
+  /**
+   * Apply arguments sent from the client to the current (possibly reused) execution
+   * sandbox.
+   */
+  applyArgumentsToSandbox: function MDA_applyArgumentsToSandbox(win, sandbox, args, command_id) {
+    try {
+      sandbox.__marionetteParams = this.curBrowser.elementManager.convertWrappedArguments(args, win);
+    }
+    catch(e) {
+      this.sendError(e.message, e.code, e.stack, command_id);
+    }
+    sandbox.__namedArgs = this.curBrowser.elementManager.applyNamedArgs(args);
+  },
+
+  /**
+   * Executes a script in the given sandbox.
+   *
+   * @param Sandbox sandbox
+   *        Sandbox in which the script will run
+   * @param string script
+   *        The script to run
+   * @param boolean directInject
+   *        If true, then the script will be run as is,
+   *        and not as a function body (as you would
+   *        do using the WebDriver spec)
+   * @param boolean async
+   *        True if the script is asynchronous
+   */
+  executeScriptInSandbox: function MDA_executeScriptInSandbox(sandbox, script,
+     directInject, async, command_id, timeout) {
+
+    if (directInject && async &&
+        (timeout == null || timeout == 0)) {
+      this.sendError("Please set a timeout", 21, null, command_id);
+      return;
+    }
+
+    if (this.importedScripts.exists()) {
+      let stream = Cc["@mozilla.org/network/file-input-stream;1"].
+                    createInstance(Ci.nsIFileInputStream);
+      stream.init(this.importedScripts, -1, 0, 0);
+      let data = NetUtil.readInputStreamToString(stream, stream.available());
+      stream.close();
+      script = data + script;
+    }
+
+    let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file", 0);
+
+    if (directInject && !async &&
+        (res == undefined || res.passed == undefined)) {
+      this.sendError("finish() not called", 500, null, command_id);
+      return;
+    }
+
+    if (!async) {
+      this.sendResponse(this.curBrowser.elementManager.wrapValue(res),
+                        command_id);
+    }
+  },
+
+  /**
+   * Execute the given script either as a function body (executeScript)
+   * or directly (for 'mochitest' like JS Marionette tests)
+   *
+   * @param object aRequest
+   *        'script' member is the script to run
+   *        'args' member holds the arguments to the script
+   * @param boolean directInject
+   *        if true, it will be run directly and not as a
+   *        function body
+   */
+  execute: function MDA_execute(aRequest, directInject) {
+    let inactivityTimeout = aRequest.parameters.inactivityTimeout;
+    let timeout = aRequest.parameters.scriptTimeout ? aRequest.parameters.scriptTimeout : this.scriptTimeout;
+    let command_id = this.command_id = this.getCommandId();
+    let script;
+    let newSandbox = aRequest.parameters.newSandbox;
+    if (newSandbox == undefined) {
+      //if client does not send a value in newSandbox,
+      //then they expect the same behaviour as webdriver
+      newSandbox = true;
+    }
+    if (this.context == "content") {
+      this.sendAsync("executeScript",
+                     {
+                       script: aRequest.parameters.script,
+                       args: aRequest.parameters.args,
+                       newSandbox: newSandbox,
+                       timeout: timeout,
+                       specialPowers: aRequest.parameters.specialPowers,
+                       filename: aRequest.parameters.filename,
+                       line: aRequest.parameters.line
+                     },
+                     command_id);
+      return;
+    }
+
+    // handle the inactivity timeout
+    let that = this;
+    if (inactivityTimeout) {
+     let inactivityTimeoutHandler = function(message, status) {
+      let error_msg = {message: value, status: status};
+      that.sendToClient({from: that.actorID, error: error_msg},
+                        marionette.command_id);
+     };
+     let setTimer = function() {
+      that.inactivityTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      if (that.inactivityTimer != null) {
+       that.inactivityTimer.initWithCallback(function() {
+        inactivityTimeoutHandler("timed out due to inactivity", 28);
+       }, inactivityTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
+      }
+     }
+     setTimer();
+     this.heartbeatCallback = function resetInactivityTimer() {
+      that.inactivityTimer.cancel();
+      setTimer();
+     }
+    }
+
+
+    let curWindow = this.getCurrentWindow();
+    if (!this.sandbox || newSandbox) {
+      let marionette = new Marionette(this, curWindow, "chrome",
+                                      this.marionetteLog,
+                                      timeout, this.heartbeatCallback, this.testName);
+      this.sandbox = this.createExecuteSandbox(curWindow,
+                                               marionette,
+                                               aRequest.parameters.specialPowers,
+                                               command_id);
+      if (!this.sandbox)
+        return;
+    }
+    this.applyArgumentsToSandbox(curWindow, this.sandbox, aRequest.parameters.args,
+                                 command_id)
+
+    try {
+      this.sandbox.finish = function chromeSandbox_finish() {
+        if (that.inactivityTimer != null) {
+          that.inactivityTimer.cancel();
+        }
+        return that.sandbox.generate_results();
+      };
+
+      if (directInject) {
+        script = aRequest.parameters.script;
+      }
+      else {
+        script = "let func = function() {" +
+                       aRequest.parameters.script +
+                     "};" +
+                     "func.apply(null, __marionetteParams);";
+      }
+      this.executeScriptInSandbox(this.sandbox, script, directInject,
+                                  false, command_id, timeout);
+    }
+    catch (e) {
+      let error = createStackMessage(e,
+                                     "execute_script",
+                                     aRequest.parameters.filename,
+                                     aRequest.parameters.line,
+                                     script);
+      this.sendError(error[0], 17, error[1], command_id);
+    }
+  },
+
+  /**
+   * Set the timeout for asynchronous script execution
+   *
+   * @param object aRequest
+   *        'ms' member is time in milliseconds to set timeout
+   */
+  setScriptTimeout: function MDA_setScriptTimeout(aRequest) {
+    this.command_id = this.getCommandId();
+    let timeout = parseInt(aRequest.parameters.ms);
+    if(isNaN(timeout)){
+      this.sendError("Not a Number", 500, null, this.command_id);
+    }
+    else {
+      this.scriptTimeout = timeout;
+      this.sendOk(this.command_id);
+    }
+  },
+
+  /**
+   * execute pure JS script. Used to execute 'mochitest'-style Marionette tests.
+   *
+   * @param object aRequest
+   *        'script' member holds the script to execute
+   *        'args' member holds the arguments to the script
+   *        'timeout' member will be used as the script timeout if it is given
+   */
+  executeJSScript: function MDA_executeJSScript(aRequest) {
+    let timeout = aRequest.parameters.scriptTimeout ? aRequest.parameters.scriptTimeout : this.scriptTimeout;
+    let command_id = this.command_id = this.getCommandId();
+
+    //all pure JS scripts will need to call Marionette.finish() to complete the test.
+    if (aRequest.newSandbox == undefined) {
+      //if client does not send a value in newSandbox,
+      //then they expect the same behaviour as webdriver
+      aRequest.newSandbox = true;
+    }
+    if (this.context == "chrome") {
+      if (aRequest.parameters.async) {
+        this.executeWithCallback(aRequest, aRequest.parameters.async);
+      }
+      else {
+        this.execute(aRequest, true);
+      }
+    }
+    else {
+      this.sendAsync("executeJSScript",
+                     {
+                       script: aRequest.parameters.script,
+                       args: aRequest.parameters.args,
+                       newSandbox: aRequest.parameters.newSandbox,
+                       async: aRequest.parameters.async,
+                       timeout: timeout,
+                       inactivityTimeout: aRequest.parameters.inactivityTimeout,
+                       specialPowers: aRequest.parameters.specialPowers,
+                       filename: aRequest.parameters.filename,
+                       line: aRequest.parameters.line,
+                     },
+                     command_id);
+   }
+  },
+
+  /**
+   * This function is used by executeAsync and executeJSScript to execute a script
+   * in a sandbox.
+   *
+   * For executeJSScript, it will return a message only when the finish() method is called.
+   * For executeAsync, it will return a response when marionetteScriptFinished/arguments[arguments.length-1]
+   * method is called, or if it times out.
+   *
+   * @param object aRequest
+   *        'script' member holds the script to execute
+   *        'args' member holds the arguments for the script
+   * @param boolean directInject
+   *        if true, it will be run directly and not as a
+   *        function body
+   */
+  executeWithCallback: function MDA_executeWithCallback(aRequest, directInject) {
+    let inactivityTimeout = aRequest.parameters.inactivityTimeout;
+    let timeout = aRequest.parameters.scriptTimeout ? aRequest.parameters.scriptTimeout : this.scriptTimeout;
+    let command_id = this.command_id = this.getCommandId();
+    let script;
+    let newSandbox = aRequest.parameters.newSandbox;
+    if (newSandbox == undefined) {
+      //if client does not send a value in newSandbox,
+      //then they expect the same behaviour as webdriver
+      newSandbox = true;
+    }
+
+    if (this.context == "content") {
+      this.sendAsync("executeAsyncScript",
+                     {
+                       script: aRequest.parameters.script,
+                       args: aRequest.parameters.args,
+                       id: this.command_id,
+                       newSandbox: newSandbox,
+                       timeout: timeout,
+                       inactivityTimeout: inactivityTimeout,
+                       specialPowers: aRequest.parameters.specialPowers,
+                       filename: aRequest.parameters.filename,
+                       line: aRequest.parameters.line
+                     },
+                     command_id);
+      return;
+    }
+
+    // handle the inactivity timeout
+    let that = this;
+    if (inactivityTimeout) {
+     this.inactivityTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+     if (this.inactivityTimer != null) {
+      this.inactivityTimer.initWithCallback(function() {
+       chromeAsyncReturnFunc("timed out due to inactivity", 28);
+      }, inactivityTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
+     }
+     this.heartbeatCallback = function resetInactivityTimer() {
+      that.inactivityTimer.cancel();
+      that.inactivityTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      if (that.inactivityTimer != null) {
+       that.inactivityTimer.initWithCallback(function() {
+        chromeAsyncReturnFunc("timed out due to inactivity", 28);
+       }, inactivityTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
+      }
+     }
+    }
+
+    let curWindow = this.getCurrentWindow();
+    let original_onerror = curWindow.onerror;
+    that.timeout = timeout;
+
+    function chromeAsyncReturnFunc(value, status, stacktrace) {
+      if (that._emu_cbs && Object.keys(that._emu_cbs).length) {
+        value = "Emulator callback still pending when finish() called";
+        status = 500;
+        that._emu_cbs = null;
+      }
+
+      if (value == undefined)
+        value = null;
+
+      if (command_id == that.command_id) {
+        if (that.timer != null) {
+          that.timer.cancel();
+          that.timer = null;
+        }
+
+        curWindow.onerror = original_onerror;
+
+        if (status == 0 || status == undefined) {
+          that.sendToClient({from: that.actorID, value: that.curBrowser.elementManager.wrapValue(value), status: status},
+                            that.command_id);
+        }
+        else {
+          let error_msg = {message: value, status: status, stacktrace: stacktrace};
+          that.sendToClient({from: that.actorID, error: error_msg},
+                            that.command_id);
+        }
+      }
+
+      if (that.inactivityTimer != null) {
+        that.inactivityTimer.cancel();
+      }
+    }
+
+    // NB: curWindow.onerror is not hooked by default due to the inability to
+    //     differentiate content exceptions from chrome exceptions. See bug
+    //     1128760 for more details. A 'debug_script' flag can be set to
+    //     reenable onerror hooking to help debug test scripts.
+    if (aRequest.parameters.debug_script) {
+      curWindow.onerror = function (errorMsg, url, lineNumber) {
+        chromeAsyncReturnFunc(errorMsg + " at: " + url + " line: " + lineNumber, 17);
+        return true;
+      };
+    }
+
+    function chromeAsyncFinish() {
+      chromeAsyncReturnFunc(that.sandbox.generate_results(), 0);
+    }
+
+    if (!this.sandbox || newSandbox) {
+      let marionette = new Marionette(this, curWindow, "chrome",
+                                      this.marionetteLog,
+                                      timeout, this.heartbeatCallback, this.testName);
+      this.sandbox = this.createExecuteSandbox(curWindow,
+                                               marionette,
+                                               aRequest.parameters.specialPowers,
+                                               command_id);
+      if (!this.sandbox)
+        return;
+    }
+    this.applyArgumentsToSandbox(curWindow, this.sandbox, aRequest.parameters.args,
+                                 command_id)
+
+    try {
+
+      this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      if (this.timer != null) {
+        this.timer.initWithCallback(function() {
+          chromeAsyncReturnFunc("timed out", 28);
+        }, that.timeout, Ci.nsITimer.TYPE_ONE_SHOT);
+      }
+
+      this.sandbox.returnFunc = chromeAsyncReturnFunc;
+      this.sandbox.finish = chromeAsyncFinish;
+
+      if (directInject) {
+        script = aRequest.parameters.script;
+      }
+      else {
+        script =  '__marionetteParams.push(returnFunc);'
+                + 'let marionetteScriptFinished = returnFunc;'
+                + 'let __marionetteFunc = function() {' + aRequest.parameters.script + '};'
+                + '__marionetteFunc.apply(null, __marionetteParams);';
+      }
+
+      this.executeScriptInSandbox(this.sandbox, script, directInject,
+                                  true, command_id, timeout);
+    } catch (e) {
+      let error = createStackMessage(e,
+                                     "execute_async_script",
+                                     aRequest.parameters.filename,
+                                     aRequest.parameters.line,
+                                     script);
+      chromeAsyncReturnFunc(error[0], 17, error[1]);
+    }
+  },
+
+  /**
+   * Navigate to to given URL.
+   *
+   * This will follow redirects issued by the server.  When the method
+   * returns is based on the page load strategy that the user has
+   * selected.
+   *
+   * Documents that contain a META tag with the "http-equiv" attribute
+   * set to "refresh" will return if the timeout is greater than 1
+   * second and the other criteria for determining whether a page is
+   * loaded are met.  When the refresh period is 1 second or less and
+   * the page load strategy is "normal" or "conservative", it will
+   * wait for the page to complete loading before returning.
+   *
+   * If any modal dialog box, such as those opened on
+   * window.onbeforeunload or window.alert, is opened at any point in
+   * the page load, it will return immediately.
+   *
+   * If a 401 response is seen by the browser, it will return
+   * immediately.  That is, if BASIC, DIGEST, NTLM or similar
+   * authentication is required, the page load is assumed to be
+   * complete.  This does not include FORM-based authentication.
+   *
+   * @param object aRequest where <code>url</code> property holds the
+   *        URL to navigate to
+   */
+  get: function MDA_get(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+
+    if (this.context != "chrome") {
+      // If a remoteness update interrupts our page load, this will never return
+      // We need to re-issue this request to correctly poll for readyState and
+      // send errors.
+      this.curBrowser.pendingCommands.push(() => {
+        aRequest.parameters.command_id = command_id;
+        this.messageManager.broadcastAsyncMessage(
+          "Marionette:pollForReadyState" + this.curBrowser.curFrameId,
+          aRequest.parameters);
+      });
+      aRequest.command_id = command_id;
+      aRequest.parameters.pageTimeout = this.pageTimeout;
+      this.sendAsync("get", aRequest.parameters, command_id);
+      return;
+    }
+
+    // At least on desktop, navigating in chrome scope does not
+    // correspond to something a user can do, and leaves marionette
+    // and the browser in an unusable state. Return a generic error insted.
+    // TODO: Error codes need to be refined as a part of bug 1100545 and
+    // bug 945729.
+    if (appName == "Firefox") {
+      this.sendError("Cannot navigate in chrome context", 13, null, command_id);
+      return;
+    }
+
+    this.getCurrentWindow().location.href = aRequest.parameters.url;
+    let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    let start = new Date().getTime();
+    let end = null;
+
+    function checkLoad() {
+      end = new Date().getTime();
+      let elapse = end - start;
+      if (this.pageTimeout == null || elapse <= this.pageTimeout){
+        if (curWindow.document.readyState == "complete") {
+          sendOk(command_id);
+          return;
+        }
+        else{
+          checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
+        }
+      }
+      else{
+        sendError("Error loading page", 13, null, command_id);
+        return;
+      }
+    }
+    checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
+  },
+
+  /**
+   * Get a string representing the current URL.
+   *
+   * On Desktop this returns a string representation of the URL of the
+   * current top level browsing context.  This is equivalent to
+   * document.location.href.
+   *
+   * When in the context of the chrome, this returns the canonical URL
+   * of the current resource.
+   */
+  getCurrentUrl: function MDA_getCurrentUrl() {
+    let isB2G = appName == "B2G";
+    this.command_id = this.getCommandId();
+    if (this.context === "chrome") {
+      this.sendResponse(this.getCurrentWindow().location.href, this.command_id);
+    }
+    else {
+      this.sendAsync("getCurrentUrl", {isB2G: isB2G}, this.command_id);
+    }
+  },
+
+  /**
+   * Gets the current title of the window
+   */
+  getTitle: function MDA_getTitle() {
+    this.command_id = this.getCommandId();
+    if (this.context == "chrome"){
+      var curWindow = this.getCurrentWindow();
+      var title = curWindow.document.documentElement.getAttribute('title');
+      this.sendResponse(title, this.command_id);
+    }
+    else {
+      this.sendAsync("getTitle", {}, this.command_id);
+    }
+  },
+
+  /**
+   * Gets the current type of the window
+   */
+  getWindowType: function MDA_getWindowType() {
+    this.command_id = this.getCommandId();
+      var curWindow = this.getCurrentWindow();
+      var type = curWindow.document.documentElement.getAttribute('windowtype');
+      this.sendResponse(type, this.command_id);
+  },
+
+  /**
+   * Gets the page source of the content document
+   */
+  getPageSource: function MDA_getPageSource(){
+    this.command_id = this.getCommandId();
+    if (this.context == "chrome"){
+      let curWindow = this.getCurrentWindow();
+      let XMLSerializer = curWindow.XMLSerializer;
+      let pageSource = new XMLSerializer().serializeToString(curWindow.document);
+      this.sendResponse(pageSource, this.command_id);
+    }
+    else {
+      this.sendAsync("getPageSource", {}, this.command_id);
+    }
+  },
+
+  /**
+   * Go back in history
+   */
+  goBack: function MDA_goBack() {
+    this.command_id = this.getCommandId();
+    this.sendAsync("goBack", {}, this.command_id);
+  },
+
+  /**
+   * Go forward in history
+   */
+  goForward: function MDA_goForward() {
+    this.command_id = this.getCommandId();
+    this.sendAsync("goForward", {}, this.command_id);
+  },
+
+  /**
+   * Refresh the page
+   */
+  refresh: function MDA_refresh() {
+    this.command_id = this.getCommandId();
+    this.sendAsync("refresh", {}, this.command_id);
+  },
+
+  /**
+   * Get the current window's handle. On desktop this typically corresponds to
+   * the currently selected tab.
+   *
+   * Return an opaque server-assigned identifier to this window that
+   * uniquely identifies it within this Marionette instance.  This can
+   * be used to switch to this window at a later point.
+   *
+   * @return unique window handle (string)
+   */
+  getWindowHandle: function MDA_getWindowHandle() {
+    this.command_id = this.getCommandId();
+    // curFrameId always holds the current tab.
+    if (this.curBrowser.curFrameId && appName != 'B2G') {
+      this.sendResponse(this.curBrowser.curFrameId, this.command_id);
+      return;
+    }
+    for (let i in this.browsers) {
+      if (this.curBrowser == this.browsers[i]) {
+        this.sendResponse(i, this.command_id);
+        return;
+      }
+    }
+  },
+
+  /**
+   * Forces an update for the given browser's id.
+   */
+  updateIdForBrowser: function (browser, newId) {
+    this._browserIds.set(browser.permanentKey, newId);
+  },
+
+  /**
+   * Retrieves a listener id for the given xul browser element. In case
+   * the browser is not known, an attempt is made to retrieve the id from
+   * a CPOW, and null is returned if this fails.
+   */
+  getIdForBrowser: function (browser) {
+    if (browser === null) {
+      return null;
+    }
+    let permKey = browser.permanentKey;
+    if (this._browserIds.has(permKey)) {
+      return this._browserIds.get(permKey);
+    }
+
+    let winId = browser.outerWindowID;
+    if (winId) {
+      winId += "";
+      this._browserIds.set(permKey, winId);
+      return winId;
+    }
+    return null;
+  },
+
+  /**
+   * Get a list of top-level browsing contexts. On desktop this typically
+   * corresponds to the set of open tabs.
+   *
+   * Each window handle is assigned by the server and is guaranteed unique,
+   * however the return array does not have a specified ordering.
+   *
+   * @return array of unique window handles as strings
+   */
+  getWindowHandles: function MDA_getWindowHandles() {
+    this.command_id = this.getCommandId();
+    let res = [];
+    let winEn = this.getWinEnumerator();
+    while (winEn.hasMoreElements()) {
+      let win = winEn.getNext();
+      if (win.gBrowser && appName != 'B2G') {
+        let tabbrowser = win.gBrowser;
+        for (let i = 0; i < tabbrowser.browsers.length; ++i) {
+          let winId = this.getIdForBrowser(tabbrowser.getBrowserAtIndex(i));
+          if (winId !== null) {
+            res.push(winId);
+          }
+        }
+      } else {
+        // XUL Windows, at least, do not have gBrowser.
+        let winId = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils)
+                       .outerWindowID;
+        winId += (appName == "B2G") ? "-b2g" : "";
+        res.push(winId);
+      }
+    }
+    this.sendResponse(res, this.command_id);
+  },
+
+  /**
+   * Get the current window's handle. This corresponds to a window that
+   * may itself contain tabs.
+   *
+   * Return an opaque server-assigned identifier to this window that
+   * uniquely identifies it within this Marionette instance.  This can
+   * be used to switch to this window at a later point.
+   *
+   * @return unique window handle (string)
+   */
+  getChromeWindowHandle: function MDA_getChromeWindowHandle() {
+    this.command_id = this.getCommandId();
+    for (let i in this.browsers) {
+      if (this.curBrowser == this.browsers[i]) {
+        this.sendResponse(i, this.command_id);
+        return;
+      }
+    }
+  },
+
+  /**
+   * Returns identifiers for each open chrome window for tests interested in
+   * managing a set of chrome windows and tabs separately.
+   *
+   * @return array of unique window handles as strings
+   */
+  getChromeWindowHandles: function MDA_getChromeWindowHandles() {
+    this.command_id = this.getCommandId();
+    let res = [];
+    let winEn = this.getWinEnumerator();
+    while (winEn.hasMoreElements()) {
+      let foundWin = winEn.getNext();
+      let winId = foundWin.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindowUtils)
+                          .outerWindowID;
+      winId = winId + ((appName == "B2G") ? "-b2g" : "");
+      res.push(winId);
+    }
+    this.sendResponse(res, this.command_id);
+  },
+
+  /**
+   * Get the current window position.
+   */
+  getWindowPosition: function MDA_getWindowPosition() {
+    this.command_id = this.getCommandId();
+    let curWindow = this.getCurrentWindow();
+    this.sendResponse({ x: curWindow.screenX, y: curWindow.screenY}, this.command_id);
+  },
+
+  /**
+  * Set the window position of the browser on the OS Window Manager
+  *
+  * @param object aRequest
+  *        'x': the x co-ordinate of the top/left of the window that
+  *             it will be moved to
+  *        'y': the y co-ordinate of the top/left of the window that
+  *             it will be moved to
+  */
+  setWindowPosition: function MDA_setWindowPosition(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (appName !== "Firefox") {
+      this.sendError("Unable to set the window position on mobile", 61, null,
+                      command_id);
+
+    }
+    else {
+      let x = parseInt(aRequest.parameters.x);;
+      let y  = parseInt(aRequest.parameters.y);
+
+      if (isNaN(x) || isNaN(y)) {
+        this.sendError("x and y arguments should be integers", 13, null, command_id);
+        return;
+      }
+      let curWindow = this.getCurrentWindow();
+      curWindow.moveTo(x, y);
+      this.sendOk(command_id);
+    }
+  },
+
+  /**
+   * Switch to a window based on name or server-assigned id.
+   * Searches based on name, then id.
+   *
+   * @param object aRequest
+   *        'name' member holds the name or id of the window to switch to
+   */
+  switchToWindow: function MDA_switchToWindow(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+
+    let checkWindow = function (win, outerId, contentWindowId, ind) {
+      if (aRequest.parameters.name == win.name ||
+          aRequest.parameters.name == contentWindowId ||
+          aRequest.parameters.name == outerId) {
+        // As in content, switching to a new window invalidates a sandbox for reuse.
+        this.sandbox = null;
+        if (this.browsers[outerId] === undefined) {
+          //enable Marionette in that browser window
+          this.startBrowser(win, false);
+        } else {
+          utils.window = win;
+          this.curBrowser = this.browsers[outerId];
+          if (contentWindowId) {
+            // The updated id corresponds to switching to a new tab.
+            this.curBrowser.switchToTab(ind);
+          }
+          this.sendOk(command_id);
+        }
+        return true;
+      }
+      return false;
+    }
+
+    let winEn = this.getWinEnumerator();
+    while (winEn.hasMoreElements()) {
+      let win = winEn.getNext();
+      let outerId = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils)
+                       .outerWindowID;
+      outerId += (appName == "B2G") ? "-b2g" : "";
+      if (win.gBrowser && appName != 'B2G') {
+        let tabbrowser = win.gBrowser;
+        for (let i = 0; i < tabbrowser.browsers.length; ++i) {
+          let browser = tabbrowser.getBrowserAtIndex(i);
+          let contentWindowId = this.getIdForBrowser(browser);
+          if (contentWindowId !== null &&
+              checkWindow.call(this, win, outerId, contentWindowId, i)) {
+            return;
+          }
+        }
+      } else {
+        // A chrome window is always a valid target for switching in the case
+        // a handle was obtained by getChromeWindowHandles.
+        if (checkWindow.call(this, win, outerId)) {
+          return;
+        }
+      }
+    }
+    this.sendError("Unable to locate window " + aRequest.parameters.name, 23, null,
+                   command_id);
+  },
+
+  getActiveFrame: function MDA_getActiveFrame() {
+    this.command_id = this.getCommandId();
+
+    if (this.context == "chrome") {
+      if (this.curFrame) {
+        let frameUid = this.curBrowser.elementManager.addToKnownElements(this.curFrame.frameElement);
+        this.sendResponse(frameUid, this.command_id);
+      } else {
+        // no current frame, we're at toplevel
+        this.sendResponse(null, this.command_id);
+      }
+    } else {
+      // not chrome
+      this.sendResponse(this.currentFrameElement, this.command_id);
+    }
+  },
+
+  /**
+   * Switch to a given frame within the current window
+   *
+   * @param object aRequest
+   *        'element' is the element to switch to
+   *        'id' if element is not set, then this
+   *                holds either the id, name or index
+   *                of the frame to switch to
+   */
+  switchToFrame: function MDA_switchToFrame(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    let curWindow = this.getCurrentWindow();
+    let checkLoad = function() {
+      let errorRegex = /about:.+(error)|(blocked)\?/;
+      let curWindow = this.getCurrentWindow();
+      if (curWindow.document.readyState == "complete") {
+        this.sendOk(command_id);
+        return;
+      }
+      else if (curWindow.document.readyState == "interactive" && errorRegex.exec(curWindow.document.baseURI)) {
+        this.sendError("Error loading page", 13, null, command_id);
+        return;
+      }
+
+      checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
+    }
+    if (this.context == "chrome") {
+      let foundFrame = null;
+      if ((aRequest.parameters.id == null) && (aRequest.parameters.element == null)) {
+        this.curFrame = null;
+        if (aRequest.parameters.focus) {
+          this.mainFrame.focus();
+        }
+        checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
+        return;
+      }
+      if (aRequest.parameters.element != undefined) {
+        if (this.curBrowser.elementManager.seenItems[aRequest.parameters.element]) {
+          let wantedFrame = this.curBrowser.elementManager.getKnownElement(aRequest.parameters.element, curWindow); //HTMLIFrameElement
+          // Deal with an embedded xul:browser case
+          if (wantedFrame.tagName == "xul:browser" || wantedFrame.tagName == "browser") {
+            curWindow = wantedFrame.contentWindow;
+            this.curFrame = curWindow;
+            if (aRequest.parameters.focus) {
+              this.curFrame.focus();
+            }
+            checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
+            return;
+          }
+          // else, assume iframe
+          let frames = curWindow.document.getElementsByTagName("iframe");
+          let numFrames = frames.length;
+          for (let i = 0; i < numFrames; i++) {
+            if (XPCNativeWrapper(frames[i]) == XPCNativeWrapper(wantedFrame)) {
+              curWindow = frames[i].contentWindow;
+              this.curFrame = curWindow;
+              if (aRequest.parameters.focus) {
+                this.curFrame.focus();
+              }
+              checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
+              return;
+          }
+        }
+      }
+    }
+    switch(typeof(aRequest.parameters.id)) {
+      case "string" :
+        let foundById = null;
+        let frames = curWindow.document.getElementsByTagName("iframe");
+        let numFrames = frames.length;
+        for (let i = 0; i < numFrames; i++) {
+          //give precedence to name
+          let frame = frames[i];
+          if (frame.getAttribute("name") == aRequest.parameters.id) {
+            foundFrame = i;
+            curWindow = frame.contentWindow;
+            break;
+          } else if ((foundById == null) && (frame.id == aRequest.parameters.id)) {
+            foundById = i;
+          }
+        }
+        if ((foundFrame == null) && (foundById != null)) {
+          foundFrame = foundById;
+          curWindow = frames[foundById].contentWindow;
+        }
+        break;
+      case "number":
+        if (curWindow.frames[aRequest.parameters.id] != undefined) {
+          foundFrame = aRequest.parameters.id;
+          curWindow = curWindow.frames[foundFrame].frameElement.contentWindow;
+        }
+        break;
+      }
+      if (foundFrame != null) {
+        this.curFrame = curWindow;
+        if (aRequest.parameters.focus) {
+          this.curFrame.focus();
+        }
+        checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
+      } else {
+        this.sendError("Unable to locate frame: " + aRequest.parameters.id, 8, null,
+                       command_id);
+      }
+    }
+    else {
+      if ((!aRequest.parameters.id) && (!aRequest.parameters.element) &&
+          (this.curBrowser.frameManager.currentRemoteFrame !== null)) {
+        // We're currently using a ChromeMessageSender for a remote frame, so this
+        // request indicates we need to switch back to the top-level (parent) frame.
+        // We'll first switch to the parent's (global) ChromeMessageBroadcaster, so
+        // we send the message to the right listener.
+        this.switchToGlobalMessageManager();
+      }
+      aRequest.command_id = command_id;
+      this.sendAsync("switchToFrame", aRequest.parameters, command_id);
+    }
+  },
+
+  /**
+   * Set timeout for searching for elements
+   *
+   * @param object aRequest
+   *        'ms' holds the search timeout in milliseconds
+   */
+  setSearchTimeout: function MDA_setSearchTimeout(aRequest) {
+    this.command_id = this.getCommandId();
+    let timeout = parseInt(aRequest.parameters.ms);
+    if (isNaN(timeout)) {
+      this.sendError("Not a Number", 500, null, this.command_id);
+    }
+    else {
+      this.searchTimeout = timeout;
+      this.sendOk(this.command_id);
+    }
+  },
+
+  /**
+   * Set timeout for page loading, searching and scripts
+   *
+   * @param object aRequest
+   *        'type' hold the type of timeout
+   *        'ms' holds the timeout in milliseconds
+   */
+  timeouts: function MDA_timeouts(aRequest){
+    /*setTimeout*/
+    this.command_id = this.getCommandId();
+    let timeout_type = aRequest.parameters.type;
+    let timeout = parseInt(aRequest.parameters.ms);
+    if (isNaN(timeout)) {
+      this.sendError("Not a Number", 500, null, this.command_id);
+    }
+    else {
+      if (timeout_type == "implicit") {
+        this.setSearchTimeout(aRequest);
+      }
+      else if (timeout_type == "script") {
+        this.setScriptTimeout(aRequest);
+      }
+      else {
+        this.pageTimeout = timeout;
+        this.sendOk(this.command_id);
+      }
+    }
+  },
+
+  /**
+   * Single Tap
+   *
+   * @param object aRequest
+            'element' represents the ID of the element to single tap on
+   */
+  singleTap: function MDA_singleTap(aRequest) {
+    this.command_id = this.getCommandId();
+    let serId = aRequest.parameters.id;
+    let x = aRequest.parameters.x;
+    let y = aRequest.parameters.y;
+    if (this.context == "chrome") {
+      this.sendError("Command 'singleTap' is not available in chrome context", 500, null, this.command_id);
+    }
+    else {
+      this.addFrameCloseListener("tap");
+      this.sendAsync("singleTap",
+                     {
+                       id: serId,
+                       corx: x,
+                       cory: y
+                     },
+                     this.command_id);
+    }
+  },
+
+  /**
+   * actionChain
+   *
+   * @param object aRequest
+   *        'value' represents a nested array: inner array represents each event; outer array represents collection of events
+   */
+  actionChain: function MDA_actionChain(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    let chain = aRequest.parameters.chain;
+    let nextId = aRequest.parameters.nextId;
+    if (this.context == "chrome") {
+      if (appName != 'Firefox') {
+        // Be conservative until this has a use case and is established to work as
+        // expected on b2g/fennec.
+        this.sendError("Command 'actionChain' is not available in chrome context",
+                       500, null, this.command_id);
+      }
+
+      let callbacks = {};
+      callbacks.onSuccess = (value) => {
+        this.sendResponse(value, command_id);
+      };
+      callbacks.onError = (message, code, trace) => {
+        this.sendError(message, code, trace, command_id);
+      };
+
+      let currWin = this.getCurrentWindow();
+      let elementManager = this.curBrowser.elementManager;
+      this.actions.dispatchActions(chain, nextId, currWin, elementManager, callbacks);
+    } else {
+      this.addFrameCloseListener("action chain");
+      this.sendAsync("actionChain",
+                     {
+                       chain: chain,
+                       nextId: nextId
+                     },
+                     command_id);
+    }
+  },
+
+  /**
+   * multiAction
+   *
+   * @param object aRequest
+   *        'value' represents a nested array: inner array represents each event;
+   *        middle array represents collection of events for each finger
+   *        outer array represents all the fingers
+   */
+
+  multiAction: function MDA_multiAction(aRequest) {
+    this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+       this.sendError("Command 'multiAction' is not available in chrome context", 500, null, this.command_id);
+    }
+    else {
+      this.addFrameCloseListener("multi action chain");
+      this.sendAsync("multiAction",
+                     {
+                       value: aRequest.parameters.value,
+                       maxlen: aRequest.parameters.max_length
+                     },
+                     this.command_id);
+   }
+ },
+
+  /**
+   * Find an element using the indicated search strategy.
+   *
+   * @param object aRequest
+   *        'using' member indicates which search method to use
+   *        'value' member is the value the client is looking for
+   */
+  findElement: function MDA_findElement(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      let id;
+      try {
+        let on_success = this.sendResponse.bind(this);
+        let on_error = this.sendError.bind(this);
+        id = this.curBrowser.elementManager.find(
+                              this.getCurrentWindow(),
+                              aRequest.parameters,
+                              this.searchTimeout,
+                              on_success,
+                              on_error,
+                              false,
+                              command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+        return;
+      }
+    }
+    else {
+      this.sendAsync("findElementContent",
+                     {
+                       value: aRequest.parameters.value,
+                       using: aRequest.parameters.using,
+                       element: aRequest.parameters.element,
+                       searchTimeout: this.searchTimeout
+                     },
+                     command_id);
+    }
+  },
+
+  /**
+   * Find element using the indicated search strategy
+   * starting from a known element. Used for WebDriver Compatibility only.
+   * @param  {object} aRequest
+   *         'using' member indicates which search method to use
+   *         'value' member is the value the client is looking for
+   *         'id' member is the value of the element to start from
+   */
+  findChildElement: function MDA_findChildElement(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    this.sendAsync("findElementContent",
+                    {
+                       value: aRequest.parameters.value,
+                       using: aRequest.parameters.using,
+                       element: aRequest.parameters.id,
+                       searchTimeout: this.searchTimeout
+                     },
+                     command_id);
+  },
+
+  /**
+   * Find elements using the indicated search strategy.
+   *
+   * @param object aRequest
+   *        'using' member indicates which search method to use
+   *        'value' member is the value the client is looking for
+   */
+  findElements: function MDA_findElements(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      let id;
+      try {
+        let on_success = this.sendResponse.bind(this);
+        let on_error = this.sendError.bind(this);
+        id = this.curBrowser.elementManager.find(this.getCurrentWindow(),
+                                                 aRequest.parameters,
+                                                 this.searchTimeout,
+                                                 on_success,
+                                                 on_error,
+                                                 true,
+                                                 command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+        return;
+      }
+    }
+    else {
+      this.sendAsync("findElementsContent",
+                     {
+                       value: aRequest.parameters.value,
+                       using: aRequest.parameters.using,
+                       element: aRequest.parameters.element,
+                       searchTimeout: this.searchTimeout
+                     },
+                     command_id);
+    }
+  },
+
+  /**
+   * Find elements using the indicated search strategy
+   * starting from a known element. Used for WebDriver Compatibility only.
+   * @param  {object} aRequest
+   *         'using' member indicates which search method to use
+   *         'value' member is the value the client is looking for
+   *         'id' member is the value of the element to start from
+   */
+  findChildElements: function MDA_findChildElement(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    this.sendAsync("findElementsContent",
+                    {
+                       value: aRequest.parameters.value,
+                       using: aRequest.parameters.using,
+                       element: aRequest.parameters.id,
+                       searchTimeout: this.searchTimeout
+                     },
+                     command_id);
+  },
+
+  /**
+   * Return the active element on the page
+   */
+  getActiveElement: function MDA_getActiveElement(){
+    let command_id = this.command_id = this.getCommandId();
+    this.sendAsync("getActiveElement", {}, command_id);
+  },
+
+  /**
+   * Send click event to element
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be clicked
+   */
+  clickElement: function MDA_clickElementent(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      try {
+        //NOTE: click atom fails, fall back to click() action
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        el.click();
+        this.sendOk(command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      // We need to protect against the click causing an OOP frame to close.
+      // This fires the mozbrowserclose event when it closes so we need to
+      // listen for it and then just send an error back. The person making the
+      // call should be aware something isnt right and handle accordingly
+      this.addFrameCloseListener("click");
+      this.sendAsync("clickElement",
+                     { id: aRequest.parameters.id },
+                     command_id);
+    }
+  },
+
+  /**
+   * Get a given attribute of an element
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be inspected
+   *        'name' member holds the name of the attribute to retrieve
+   */
+  getElementAttribute: function MDA_getElementAttribute(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      try {
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        this.sendResponse(utils.getElementAttribute(el, aRequest.parameters.name),
+                          command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("getElementAttribute",
+                     {
+                       id: aRequest.parameters.id,
+                       name: aRequest.parameters.name
+                     },
+                     command_id);
+    }
+  },
+
+  /**
+   * Get the text of an element, if any. Includes the text of all child elements.
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be inspected
+   */
+  getElementText: function MDA_getElementText(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      //Note: for chrome, we look at text nodes, and any node with a "label" field
+      try {
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        let lines = [];
+        this.getVisibleText(el, lines);
+        lines = lines.join("\n");
+        this.sendResponse(lines, command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("getElementText",
+                     { id: aRequest.parameters.id },
+                     command_id);
+    }
+  },
+
+  /**
+   * Get the tag name of the element.
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be inspected
+   */
+  getElementTagName: function MDA_getElementTagName(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      try {
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        this.sendResponse(el.tagName.toLowerCase(), command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("getElementTagName",
+                     { id: aRequest.parameters.id },
+                     command_id);
+    }
+  },
+
+  /**
+   * Check if element is displayed
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be checked
+   */
+  isElementDisplayed: function MDA_isElementDisplayed(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      try {
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        this.sendResponse(utils.isElementDisplayed(el), command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("isElementDisplayed",
+                     { id:aRequest.parameters.id },
+                     command_id);
+    }
+  },
+
+  /**
+   * Return the property of the computed style of an element
+   *
+   * @param object aRequest
+   *               'id' member holds the reference id to
+   *               the element that will be checked
+   *               'propertyName' is the CSS rule that is being requested
+   */
+  getElementValueOfCssProperty: function MDA_getElementValueOfCssProperty(aRequest){
+    let command_id = this.command_id = this.getCommandId();
+    let curWin = this.getCurrentWindow();
+    if (this.context == "chrome") {
+      try {
+        let el = this.curBrowser.elementManager.getKnownElement(aRequest.parameters.id, curWin);
+        this.sendResponse(curWin.document.defaultView.getComputedStyle(el, null).getPropertyValue(
+          aRequest.parameters.propertyName), command_id);
+      } catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("getElementValueOfCssProperty",
+                     {id: aRequest.parameters.id, propertyName: aRequest.parameters.propertyName},
+                     command_id);
+    }
+  },
+
+  /**
+   * Submit a form on a content page by either using form or element in a form
+   * @param object aRequest
+   *               'id' member holds the reference id to
+   *               the element that will be checked
+  */
+  submitElement: function MDA_submitElement(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      this.sendError("Command 'submitElement' is not available in chrome context", 500, null, this.command_id);
+    }
+    else {
+      this.sendAsync("submitElement", {id: aRequest.parameters.id}, command_id);
+    }
+  },
+
+  /**
+   * Check if element is enabled
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be checked
+   */
+  isElementEnabled: function(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    let id = aRequest.parameters.id;
+    if (this.context == "chrome") {
+      try {
+        // Selenium atom doesn't quite work here
+        let win = this.getCurrentWindow();
+        let el = this.curBrowser.elementManager.getKnownElement(id, win);
+        this.sendResponse(!!!el.disabled, command_id);
+      } catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    } else {
+      this.sendAsync("isElementEnabled", {id: id}, command_id);
+    }
+  },
+
+  /**
+   * Check if element is selected
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be checked
+   */
+  isElementSelected: function MDA_isElementSelected(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      try {
+        //Selenium atom doesn't quite work here
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        if (el.checked != undefined) {
+          this.sendResponse(!!el.checked, command_id);
+        }
+        else if (el.selected != undefined) {
+          this.sendResponse(!!el.selected, command_id);
+        }
+        else {
+          this.sendResponse(true, command_id);
+        }
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("isElementSelected",
+                     { id:aRequest.parameters.id },
+                     command_id);
+    }
+  },
+
+  getElementSize: function MDA_getElementSize(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      try {
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        let clientRect = el.getBoundingClientRect();
+        this.sendResponse({width: clientRect.width, height: clientRect.height},
+                          command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("getElementSize",
+                     { id:aRequest.parameters.id },
+                     command_id);
+    }
+  },
+
+  getElementRect: function MDA_getElementRect(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      try {
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        let clientRect = el.getBoundingClientRect();
+        this.sendResponse({x: clientRect.x + this.getCurrentWindow().pageXOffset,
+                           y: clientRect.y + this.getCurrentWindow().pageYOffset,
+                           width: clientRect.width, height: clientRect.height},
+                           command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("getElementRect",
+                     { id:aRequest.parameters.id },
+                     command_id);
+    }
+  },
+
+  /**
+   * Send key presses to element after focusing on it
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be checked
+   *        'value' member holds the value to send to the element
+   */
+  sendKeysToElement: function MDA_sendKeysToElement(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      let currentWindow = this.getCurrentWindow();
+      let el = this.curBrowser.elementManager.getKnownElement(
+        aRequest.parameters.id, currentWindow);
+      utils.sendKeysToElement(currentWindow, el, aRequest.parameters.value,
+                              this.sendOk.bind(this), this.sendError.bind(this),
+                              command_id, this.context);
+    }
+    else {
+      this.sendAsync("sendKeysToElement",
+                     {
+                       id:aRequest.parameters.id,
+                       value: aRequest.parameters.value
+                     },
+                     command_id);
+    }
+  },
+
+  /**
+   * Sets the test name
+   *
+   * The test name is used in logging messages.
+   */
+  setTestName: function MDA_setTestName(aRequest) {
+    this.command_id = this.getCommandId();
+    this.testName = aRequest.parameters.value;
+    this.sendAsync("setTestName",
+                   { value: aRequest.parameters.value },
+                   this.command_id);
+  },
+
+  /**
+   * Clear the text of an element
+   *
+   * @param object aRequest
+   *        'id' member holds the reference id to
+   *        the element that will be cleared
+   */
+  clearElement: function MDA_clearElement(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      //the selenium atom doesn't work here
+      try {
+        let el = this.curBrowser.elementManager.getKnownElement(
+            aRequest.parameters.id, this.getCurrentWindow());
+        if (el.nodeName == "textbox") {
+          el.value = "";
+        }
+        else if (el.nodeName == "checkbox") {
+          el.checked = false;
+        }
+        this.sendOk(command_id);
+      }
+      catch (e) {
+        this.sendError(e.message, e.code, e.stack, command_id);
+      }
+    }
+    else {
+      this.sendAsync("clearElement",
+                     { id:aRequest.parameters.id },
+                     command_id);
+    }
+  },
+
+  /**
+   * Get an element's location on the page.
+   *
+   * The returned point will contain the x and y coordinates of the
+   * top left-hand corner of the given element.  The point (0,0)
+   * refers to the upper-left corner of the document.
+   *
+   * @return a point containing x and y coordinates as properties
+   */
+  getElementLocation: function MDA_getElementLocation(aRequest) {
+    this.command_id = this.getCommandId();
+    this.sendAsync("getElementLocation", {id: aRequest.parameters.id},
+                   this.command_id);
+  },
+
+  /**
+   * Add a cookie to the document.
+   */
+  addCookie: function MDA_addCookie(aRequest) {
+    this.command_id = this.getCommandId();
+    this.sendAsync("addCookie",
+                   { cookie:aRequest.parameters.cookie },
+                   this.command_id);
+  },
+
+  /**
+   * Get all the cookies for the current domain.
+   *
+   * This is the equivalent of calling "document.cookie" and parsing
+   * the result.
+   */
+  getCookies: function MDA_getCookies() {
+    this.command_id = this.getCommandId();
+    this.sendAsync("getCookies", {}, this.command_id);
+  },
+
+  /**
+   * Delete all cookies that are visible to a document
+   */
+  deleteAllCookies: function MDA_deleteAllCookies() {
+    this.command_id = this.getCommandId();
+    this.sendAsync("deleteAllCookies", {}, this.command_id);
+  },
+
+  /**
+   * Delete a cookie by name
+   */
+  deleteCookie: function MDA_deleteCookie(aRequest) {
+    this.command_id = this.getCommandId();
+    this.sendAsync("deleteCookie",
+                   { name:aRequest.parameters.name },
+                   this.command_id);
+  },
+
+  /**
+   * Close the current window, ending the session if it's the last
+   * window currently open.
+   *
+   * On B2G this method is a noop and will return immediately.
+   */
+  close: function MDA_close() {
+    let command_id = this.command_id = this.getCommandId();
+    if (appName == "B2G") {
+      // We can't close windows so just return
+      this.sendOk(command_id);
+    }
+    else {
+      // Get the total number of windows
+      let numOpenWindows = 0;
+      let winEnum = this.getWinEnumerator();
+      while (winEnum.hasMoreElements()) {
+        let win = winEnum.getNext();
+        // Return windows and tabs.
+        if (win.gBrowser) {
+          numOpenWindows += win.gBrowser.browsers.length;
+        } else {
+          numOpenWindows += 1;
+        }
+      }
+
+      // if there is only 1 window left, delete the session
+      if (numOpenWindows === 1) {
+        try {
+          this.sessionTearDown();
+        }
+        catch (e) {
+          this.sendError("Could not clear session", 500,
+                         e.name + ": " + e.message, command_id);
+          return;
+        }
+        this.sendOk(command_id);
+        return;
+      }
+
+      try {
+        if (this.messageManager != this.globalMessageManager) {
+          this.messageManager.removeDelayedFrameScript(FRAME_SCRIPT);
+        }
+        if (this.curBrowser.tab) {
+          this.curBrowser.closeTab();
+        } else {
+          this.getCurrentWindow().close();
+        }
+        this.sendOk(command_id);
+      }
+      catch (e) {
+        this.sendError("Could not close window: " + e.message, 13, e.stack,
+                       command_id);
+      }
+    }
+  },
+
+  /**
+   * Close the currently selected chrome window, ending the session if it's the last
+   * window currently open.
+   *
+   * On B2G this method is a noop and will return immediately.
+   */
+  closeChromeWindow: function MDA_closeChromeWindow() {
+    let command_id = this.command_id = this.getCommandId();
+    if (appName == "B2G") {
+      // We can't close windows so just return
+      this.sendOk(command_id);
+    }
+    else {
+      // Get the total number of windows
+      let numOpenWindows = 0;
+      let winEnum = this.getWinEnumerator();
+      while (winEnum.hasMoreElements()) {
+        numOpenWindows += 1;
+        winEnum.getNext();
+      }
+
+      // if there is only 1 window left, delete the session
+      if (numOpenWindows === 1) {
+        try {
+          this.sessionTearDown();
+        }
+        catch (e) {
+          this.sendError("Could not clear session", 500,
+                         e.name + ": " + e.message, command_id);
+          return;
+        }
+        this.sendOk(command_id);
+        return;
+      }
+
+      try {
+        this.messageManager.removeDelayedFrameScript(FRAME_SCRIPT);
+        this.getCurrentWindow().close();
+        this.sendOk(command_id);
+      }
+      catch (e) {
+        this.sendError("Could not close window: " + e.message, 13, e.stack,
+                       command_id);
+      }
+    }
+  },
+
+  /**
+   * Deletes the session.
+   *
+   * If it is a desktop environment, it will close all listeners
+   *
+   * If it is a B2G environment, it will make the main content listener sleep, and close
+   * all other listeners. The main content listener persists after disconnect (it's the homescreen),
+   * and can safely be reused.
+   */
+  sessionTearDown: function MDA_sessionTearDown() {
+    if (this.curBrowser != null) {
+      if (appName == "B2G") {
+        this.globalMessageManager.broadcastAsyncMessage(
+            "Marionette:sleepSession" + this.curBrowser.mainContentId, {});
+        this.curBrowser.knownFrames.splice(
+            this.curBrowser.knownFrames.indexOf(this.curBrowser.mainContentId), 1);
+      }
+      else {
+        //don't set this pref for B2G since the framescript can be safely reused
+        Services.prefs.setBoolPref("marionette.contentListener", false);
+      }
+      //delete session in each frame in each browser
+      for (let win in this.browsers) {
+        let browser = this.browsers[win];
+        for (let i in browser.knownFrames) {
+          this.globalMessageManager.broadcastAsyncMessage("Marionette:deleteSession" + browser.knownFrames[i], {});
+        }
+      }
+      let winEnum = this.getWinEnumerator();
+      while (winEnum.hasMoreElements()) {
+        winEnum.getNext().messageManager.removeDelayedFrameScript(FRAME_SCRIPT);
+      }
+      this.curBrowser.frameManager.removeSpecialPowers();
+      this.curBrowser.frameManager.removeMessageManagerListeners(this.globalMessageManager);
+    }
+    this.switchToGlobalMessageManager();
+    // reset frame to the top-most frame
+    this.curFrame = null;
+    if (this.mainFrame) {
+      this.mainFrame.focus();
+    }
+    this.sessionId = null;
+    this.deleteFile('marionetteChromeScripts');
+    this.deleteFile('marionetteContentScripts');
+
+    if (this.observing !== null) {
+      for (let topic in this.observing) {
+        Services.obs.removeObserver(this.observing[topic], topic);
+      }
+      this.observing = null;
+    }
+  },
+
+  /**
+   * Processes the 'deleteSession' request from the client by tearing down
+   * the session and responding 'ok'.
+   */
+  deleteSession: function MDA_deleteSession() {
+    let command_id = this.command_id = this.getCommandId();
+    try {
+      this.sessionTearDown();
+    }
+    catch (e) {
+      this.sendError("Could not delete session", 500, e.name + ": " + e.message, command_id);
+      return;
+    }
+    this.sendOk(command_id);
+  },
+
+  /**
+   * Quits the application with the provided flags and tears down the
+   * current session.
+   */
+  quitApplication: function MDA_quitApplication (aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    if (appName != "Firefox") {
+      this.sendError("In app initiated quit only supported on Firefox", 500, null, command_id);
+    }
+
+    let flagsArray = aRequest.parameters.flags;
+    let flags = Ci.nsIAppStartup.eAttemptQuit;
+    for (let k of flagsArray) {
+      flags |= Ci.nsIAppStartup[k];
+    }
+
+    // Close the listener so we can't re-connect until after the restart.
+    this.server.closeListener();
+    this.quitFlags = flags;
+
+    // This notifies the client it's safe to begin attempting to reconnect.
+    // The actual quit will happen when the current socket connection is closed.
+    this.sendOk(command_id);
+  },
+
+  /**
+   * Returns the current status of the Application Cache
+   */
+  getAppCacheStatus: function MDA_getAppCacheStatus(aRequest) {
+    this.command_id = this.getCommandId();
+    this.sendAsync("getAppCacheStatus", {}, this.command_id);
+  },
+
+  _emu_cb_id: 0,
+  _emu_cbs: null,
+  runEmulatorCmd: function runEmulatorCmd(cmd, callback) {
+    if (callback) {
+      if (!this._emu_cbs) {
+        this._emu_cbs = {};
+      }
+      this._emu_cbs[this._emu_cb_id] = callback;
+    }
+    this.sendToClient({emulator_cmd: cmd, id: this._emu_cb_id}, -1);
+    this._emu_cb_id += 1;
+  },
+
+  runEmulatorShell: function runEmulatorShell(args, callback) {
+    if (callback) {
+      if (!this._emu_cbs) {
+        this._emu_cbs = {};
+      }
+      this._emu_cbs[this._emu_cb_id] = callback;
+    }
+    this.sendToClient({emulator_shell: args, id: this._emu_cb_id}, -1);
+    this._emu_cb_id += 1;
+  },
+
+  emulatorCmdResult: function emulatorCmdResult(message) {
+    if (this.context != "chrome") {
+      this.sendAsync("emulatorCmdResult", message, -1);
+      return;
+    }
+
+    if (!this._emu_cbs) {
+      return;
+    }
+
+    let cb = this._emu_cbs[message.id];
+    delete this._emu_cbs[message.id];
+    if (!cb) {
+      return;
+    }
+    try {
+      cb(message.result);
+    }
+    catch(e) {
+      this.sendError(e.message, e.code, e.stack, -1);
+      return;
+    }
+  },
+
+  importScript: function MDA_importScript(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    let converter =
+      Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
+          createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
+    converter.charset = "UTF-8";
+    let result = {};
+    let data = converter.convertToByteArray(aRequest.parameters.script, result);
+    let ch = Components.classes["@mozilla.org/security/hash;1"]
+                       .createInstance(Components.interfaces.nsICryptoHash);
+    ch.init(ch.MD5);
+    ch.update(data, data.length);
+    let hash = ch.finish(true);
+    if (this.importedScriptHashes[this.context].indexOf(hash) > -1) {
+        //we have already imported this script
+        this.sendOk(command_id);
+        return;
+    }
+    this.importedScriptHashes[this.context].push(hash);
+    if (this.context == "chrome") {
+      let file;
+      if (this.importedScripts.exists()) {
+        file = FileUtils.openFileOutputStream(this.importedScripts,
+            FileUtils.MODE_APPEND | FileUtils.MODE_WRONLY);
+      }
+      else {
+        //Note: The permission bits here don't actually get set (bug 804563)
+        this.importedScripts.createUnique(
+            Components.interfaces.nsIFile.NORMAL_FILE_TYPE, parseInt("0666", 8));
+        file = FileUtils.openFileOutputStream(this.importedScripts,
+            FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
+        this.importedScripts.permissions = parseInt("0666", 8); //actually set permissions
+      }
+      file.write(aRequest.parameters.script, aRequest.parameters.script.length);
+      file.close();
+      this.sendOk(command_id);
+    }
+    else {
+      this.sendAsync("importScript",
+                     { script: aRequest.parameters.script },
+                     command_id);
+    }
+  },
+
+  clearImportedScripts: function MDA_clearImportedScripts(aRequest) {
+    let command_id = this.command_id = this.getCommandId();
+    try {
+      if (this.context == "chrome") {
+        this.deleteFile('marionetteChromeScripts');
+      }
+      else {
+        this.deleteFile('marionetteContentScripts');
+      }
+    }
+    catch (e) {
+      this.sendError("Could not clear imported scripts", 500, e.name + ": " + e.message, command_id);
+      return;
+    }
+    this.sendOk(command_id);
+  },
+
+  /**
+   * Takes a screenshot of a web element, current frame, or viewport.
+   *
+   * The screen capture is returned as a lossless PNG image encoded as
+   * a base 64 string.
+   *
+   * If called in the content context, the <code>id</code> argument is not null
+   * and refers to a present and visible web element's ID, the capture area
+   * will be limited to the bounding box of that element. Otherwise, the
+   * capture area will be the bounding box of the current frame.
+   *
+   * If called in the chrome context, the screenshot will always represent the
+   * entire viewport.
+   *
+   * @param {string} [id] Reference to a web element.
+   * @param {string} [highlights] List of web elements to highlight.
+   * @return {string} PNG image encoded as base 64 string.
+   */
+  takeScreenshot: function MDA_takeScreenshot(aRequest) {
+    this.command_id = this.getCommandId();
+    if (this.context == "chrome") {
+      var win = this.getCurrentWindow();
+      var canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+      var doc;
+      if (appName == "B2G") {
+        doc = win.document.body;
+      } else {
+        doc = win.document.getElementsByTagName('window')[0];
+      }
+      var docRect = doc.getBoundingClientRect();
+      var width = docRect.width;
+      var height = docRect.height;
+
+      // Convert width and height from CSS pixels (potentially fractional)
+      // to device pixels (integer).
+      var scale = win.devicePixelRatio;
+      canvas.setAttribute("width", Math.round(width * scale));
+      canvas.setAttribute("height", Math.round(height * scale));
+
+      var context = canvas.getContext("2d");
+      var flags;
+      if (appName == "B2G") {
+        flags =
+          context.DRAWWINDOW_DRAW_CARET |
+          context.DRAWWINDOW_DRAW_VIEW |
+          context.DRAWWINDOW_USE_WIDGET_LAYERS;
+      } else {
+        // Bug 1075168 - CanvasRenderingContext2D image is distorted
+        // when using certain flags in chrome context.
+        flags =
+          context.DRAWWINDOW_DRAW_VIEW |
+          context.DRAWWINDOW_USE_WIDGET_LAYERS;
+      }
+      context.scale(scale, scale);
+      context.drawWindow(win, 0, 0, width, height, "rgb(255,255,255)", flags);
+      var dataUrl = canvas.toDataURL("image/png", "");
+      var data = dataUrl.substring(dataUrl.indexOf(",") + 1);
+      this.sendResponse(data, this.command_id);
+    }
+    else {
+      this.sendAsync("takeScreenshot",
+                   {id: aRequest.parameters.id,
+                    highlights: aRequest.parameters.highlights,
+                    full: aRequest.parameters.full},
+                   this.command_id);
+    }
+  },
+
+  /**
+   * Get the current browser orientation.
+   *
+   * Will return one of the valid primary orientation values
+   * portrait-primary, landscape-primary, portrait-secondary, or
+   * landscape-secondary.
+   */
+  getScreenOrientation: function MDA_getScreenOrientation(aRequest) {
+    this.command_id = this.getCommandId();
+    let curWindow = this.getCurrentWindow();
+    let or = curWindow.screen.mozOrientation;
+    this.sendResponse(or, this.command_id);
+  },
+
+  /**
+   * Set the current browser orientation.
+   *
+   * The supplied orientation should be given as one of the valid
+   * orientation values.  If the orientation is unknown, an error will
+   * be raised.
+   *
+   * Valid orientations are "portrait" and "landscape", which fall
+   * back to "portrait-primary" and "landscape-primary" respectively,
+   * and "portrait-secondary" as well as "landscape-secondary".
+   */
+  setScreenOrientation: function MDA_setScreenOrientation(aRequest) {
+    const ors = ["portrait", "landscape",
+                 "portrait-primary", "landscape-primary",
+                 "portrait-secondary", "landscape-secondary"];
+
+    this.command_id = this.getCommandId();
+    let or = String(aRequest.parameters.orientation);
+
+    let mozOr = or.toLowerCase();
+    if (ors.indexOf(mozOr) < 0) {
+      this.sendError("Unknown screen orientation: " + or, 500, null,
+                     this.command_id);
+      return;
+    }
+
+    let curWindow = this.getCurrentWindow();
+    if (!curWindow.screen.mozLockOrientation(mozOr)) {
+      this.sendError("Unable to set screen orientation: " + or, 500,
+                     null, this.command_id);
+    }
+    this.sendOk(this.command_id);
+  },
+
+  /**
+   * Get the size of the browser window currently in focus.
+   *
+   * Will return the current browser window size in pixels. Refers to
+   * window outerWidth and outerHeight values, which include scroll bars,
+   * title bars, etc.
+   *
+   */
+  getWindowSize: function MDA_getWindowSize(aRequest) {
+    this.command_id = this.getCommandId();
+    let curWindow = this.getCurrentWindow();
+    let curWidth = curWindow.outerWidth;
+    let curHeight = curWindow.outerHeight;
+    this.sendResponse({width: curWidth, height: curHeight}, this.command_id);
+  },
+
+  /**
+   * Set the size of the browser window currently in focus.
+   *
+   * Not supported on B2G. The supplied width and height values refer to
+   * the window outerWidth and outerHeight values, which include scroll
+   * bars, title bars, etc.
+   *
+   * An error will be returned if the requested window size would result
+   * in the window being in the maximized state.
+   */
+  setWindowSize: function MDA_setWindowSize(aRequest) {
+    this.command_id = this.getCommandId();
+
+    if (appName !== "Firefox") {
+      this.sendError("Not supported on mobile", 405, null, this.command_id);
+      return;
+    }
+
+    try {
+      var width = parseInt(aRequest.parameters.width);
+      var height = parseInt(aRequest.parameters.height);
+    }
+    catch(e) {
+      this.sendError(e.message, e.code, e.stack, this.command_id);
+      return;
+    }
+
+    let curWindow = this.getCurrentWindow();
+    if (width >= curWindow.screen.availWidth && height >= curWindow.screen.availHeight) {
+      this.sendError("Invalid requested size, cannot maximize", 405, null, this.command_id);
+      return;
+    }
+
+    curWindow.resizeTo(width, height);
+    this.sendOk(this.command_id);
+  },
+
+  /**
+   * Maximizes the Browser Window as if the user pressed the maximise button
+   *
+   * Not Supported on B2G or Fennec
+   */
+  maximizeWindow: function MDA_maximizeWindow (aRequest) {
+    this.command_id = this.getCommandId();
+
+    if (appName !== "Firefox") {
+      this.sendError("Not supported for mobile", 405, null, this.command_id);
+      return;
+    }
+
+    let curWindow = this.getCurrentWindow();
+    curWindow.moveTo(0,0);
+    curWindow.resizeTo(curWindow.screen.availWidth, curWindow.screen.availHeight);
+    this.sendOk(this.command_id);
+  },
+
+  /**
+   * Returns the ChromeWindow associated with an open dialog window if it is
+   * currently attached to the dom.
+   */
+  get activeDialogWindow () {
+    if (this._dialogWindowRef !== null) {
+      let dialogWin = this._dialogWindowRef.get();
+      if (dialogWin && dialogWin.parent) {
+        return dialogWin;
+      }
+    }
+    return null;
+  },
+
+  get activeDialogUI () {
+    let dialogWin = this.activeDialogWindow;
+    if (dialogWin) {
+      return dialogWin.Dialog.ui;
+    }
+    return this.curBrowser.getTabModalUI();
+  },
+
+  /**
+   * Dismisses a currently displayed tab modal, or returns no such alert if
+   * no modal is displayed.
+   */
+  dismissDialog: function MDA_dismissDialog() {
+    this.command_id = this.getCommandId();
+    if (this.activeDialogUI === null) {
+      this.sendError("No tab modal was open when attempting to dismiss the dialog",
+                     27, null, this.command_id);
+      return;
+    }
+
+    let {button0, button1} = this.activeDialogUI;
+    (button1 ? button1 : button0).click();
+    this.sendOk(this.command_id);
+  },
+
+  /**
+   * Accepts a currently displayed tab modal, or returns no such alert if
+   * no modal is displayed.
+   */
+  acceptDialog: function MDA_acceptDialog() {
+    this.command_id = this.getCommandId();
+    if (this.activeDialogUI === null) {
+      this.sendError("No tab modal was open when attempting to accept the dialog",
+                     27, null, this.command_id);
+      return;
+    }
+
+    let {button0} = this.activeDialogUI;
+    button0.click();
+    this.sendOk(this.command_id);
+  },
+
+  /**
+   * Returns the message shown in a currently displayed modal, or returns a no such
+   * alert error if no modal is currently displayed.
+   */
+  getTextFromDialog: function MDA_getTextFromDialog() {
+    this.command_id = this.getCommandId();
+    if (this.activeDialogUI === null) {
+      this.sendError("No tab modal was open when attempting to get the dialog text",
+                     27, null, this.command_id);
+      return;
+    }
+
+    let {infoBody} = this.activeDialogUI;
+    this.sendResponse(infoBody.textContent, this.command_id);
+  },
+
+  /**
+   * Sends keys to the input field of a currently displayed modal, or returns a
+   * no such alert error if no modal is currently displayed. If a tab modal is currently
+   * displayed but has no means for text input, an element not visible error is returned.
+   */
+  sendKeysToDialog: function MDA_sendKeysToDialog(aRequest) {
+    this.command_id = this.getCommandId();
+    if (this.activeDialogUI === null) {
+      this.sendError("No tab modal was open when attempting to send keys to a dialog",
+                     27, null, this.command_id);
+      return;
+    }
+
+    // See toolkit/components/prompts/contentb/commonDialog.js
+    let {loginContainer, loginTextbox} = this.activeDialogUI;
+    if (loginContainer.hidden) {
+      this.sendError("This prompt does not accept text input",
+                     11, null, this.command_id);
+    }
+
+    let win = this.activeDialogWindow ? this.activeDialogWindow : this.getCurrentWindow();
+    utils.sendKeysToElement(win, loginTextbox, aRequest.parameters.value,
+                            this.sendOk.bind(this), this.sendError.bind(this),
+                            this.command_id, "chrome");
+  },
+
+  /**
+   * Helper function to convert an outerWindowID into a UID that Marionette
+   * tracks.
+   */
+  generateFrameId: function MDA_generateFrameId(id) {
+    let uid = id + (appName == "B2G" ? "-b2g" : "");
+    return uid;
+  },
+
+  /**
+   * Handle a dialog opening by shortcutting the current request to prevent the client
+   * from hanging entirely. This is inspired by selenium's mode of dealing with this,
+   * but is significantly lighter weight, and may necessitate a different framework
+   * for handling this as more features are required.
+   */
+  handleDialogLoad: function MDA_handleModalLoad(subject, topic) {
+    // We shouldn't return to the client due to the modal associated with the
+    // jsdebugger.
+    let clickToStart;
+    try {
+      clickToStart = Services.prefs.getBoolPref('marionette.debugging.clicktostart');
+    } catch (e) { }
+    if (clickToStart) {
+      Services.prefs.setBoolPref('marionette.debugging.clicktostart', false);
+      return;
+    }
+
+    if (topic == "common-dialog-loaded") {
+      this._dialogWindowRef = Cu.getWeakReference(subject);
+    }
+
+    if (this.command_id) {
+      this.sendAsync("cancelRequest", {});
+      // This is a shortcut to get the client to accept our response whether
+      // the expected key is 'ok' (in case a click or similar got us here)
+      // or 'value' (in case an execute script or similar got us here).
+      this.sendToClient({from:this.actorID, ok: true, value: null}, this.command_id);
+    }
+  },
+
+  /**
+   * Receives all messages from content messageManager
+   */
+  receiveMessage: function MDA_receiveMessage(message) {
+    // We need to just check if we need to remove the mozbrowserclose listener
+    if (this.mozBrowserClose !== null){
+      let curWindow = this.getCurrentWindow();
+      curWindow.removeEventListener('mozbrowserclose', this.mozBrowserClose, true);
+      this.mozBrowserClose = null;
+    }
+
+    switch (message.name) {
+      case "Marionette:done":
+        this.sendResponse(message.json.value, message.json.command_id);
+        break;
+      case "Marionette:ok":
+        this.sendOk(message.json.command_id);
+        break;
+      case "Marionette:error":
+        this.sendError(message.json.message, message.json.status, message.json.stacktrace, message.json.command_id);
+        break;
+      case "Marionette:log":
+        //log server-side messages
+        logger.info(message.json.message);
+        break;
+      case "Marionette:shareData":
+        //log messages from tests
+        if (message.json.log) {
+          this.marionetteLog.addLogs(message.json.log);
+        }
+        break;
+      case "Marionette:runEmulatorCmd":
+      case "Marionette:runEmulatorShell":
+        this.sendToClient(message.json, -1);
+        break;
+      case "Marionette:switchToFrame":
+        this.oopFrameId = this.curBrowser.frameManager.switchToFrame(message);
+        this.messageManager = this.curBrowser.frameManager.currentRemoteFrame.messageManager.get();
+        break;
+      case "Marionette:switchToModalOrigin":
+        this.curBrowser.frameManager.switchToModalOrigin(message);
+        this.messageManager = this.curBrowser.frameManager.currentRemoteFrame.messageManager.get();
+        break;
+      case "Marionette:switchedToFrame":
+        logger.info("Switched to frame: " + JSON.stringify(message.json));
+        if (message.json.restorePrevious) {
+          this.currentFrameElement = this.previousFrameElement;
+        }
+        else {
+          if (message.json.storePrevious) {
+            // we don't arbitrarily save previousFrameElement, since
+            // we allow frame switching after modals appear, which would
+            // override this value and we'd lose our reference
+            this.previousFrameElement = this.currentFrameElement;
+          }
+          this.currentFrameElement = message.json.frameValue;
+        }
+        break;
+      case "Marionette:getVisibleCookies":
+        let [currentPath, host] = message.json.value;
+        let isForCurrentPath = function(aPath) {
+          return currentPath.indexOf(aPath) != -1;
+        }
+        let results = [];
+        let enumerator = cookieManager.enumerator;
+        while (enumerator.hasMoreElements()) {
+          let cookie = enumerator.getNext().QueryInterface(Ci['nsICookie']);
+          // Take the hostname and progressively shorten
+          let hostname = host;
+          do {
+            if ((cookie.host == '.' + hostname || cookie.host == hostname)
+                && isForCurrentPath(cookie.path)) {
+              results.push({
+                'name': cookie.name,
+                'value': cookie.value,
+                'path': cookie.path,
+                'host': cookie.host,
+                'secure': cookie.isSecure,
+                'expiry': cookie.expires
+              });
+              break;
+            }
+            hostname = hostname.replace(/^.*?\./, '');
+          } while (hostname.indexOf('.') != -1);
+        }
+        return results;
+      case "Marionette:addCookie":
+        let cookieToAdd = message.json.value;
+        Services.cookies.add(cookieToAdd.domain, cookieToAdd.path, cookieToAdd.name,
+                             cookieToAdd.value, cookieToAdd.secure, false, false,
+                             cookieToAdd.expiry);
+        return true;
+      case "Marionette:deleteCookie":
+        let cookieToDelete = message.json.value;
+        cookieManager.remove(cookieToDelete.host, cookieToDelete.name,
+                             cookieToDelete.path, false);
+        return true;
+      case "Marionette:register":
+        // This code processes the content listener's registration information
+        // and either accepts the listener, or ignores it
+        let nullPrevious = (this.curBrowser.curFrameId == null);
+        let listenerWindow = null;
+        try {
+          listenerWindow = Services.wm.getOuterWindowWithId(message.json.value);
+        } catch (ex) { }
+
+        //go in here if we're already in a remote frame.
+        if (this.curBrowser.frameManager.currentRemoteFrame !== null &&
+            (!listenerWindow ||
+             this.messageManager == this.curBrowser.frameManager.currentRemoteFrame.messageManager.get())) {
+          // The outerWindowID from an OOP frame will not be meaningful to
+          // the parent process here, since each process maintains its own
+          // independent window list.  So, it will either be null (!listenerWindow)
+          // if we're already in a remote frame,
+          // or it will point to some random window, which will hopefully
+          // cause an href mismatch.  Currently this only happens
+          // in B2G for OOP frames registered in Marionette:switchToFrame, so
+          // we'll acknowledge the switchToFrame message here.
+          // XXX: Should have a better way of determining that this message
+          // is from a remote frame.
+          this.curBrowser.frameManager.currentRemoteFrame.targetFrameId = this.generateFrameId(message.json.value);
+          this.sendOk(this.command_id);
+        }
+
+        let browserType;
+        try {
+          browserType = message.target.getAttribute("type");
+        } catch (ex) {
+          // browserType remains undefined.
+        }
+        let reg = {};
+        // this will be sent to tell the content process if it is the main content
+        let mainContent = (this.curBrowser.mainContentId == null);
+        if (!browserType || browserType != "content") {
+          //curBrowser holds all the registered frames in knownFrames
+          let uid = this.generateFrameId(message.json.value);
+          reg.id = uid;
+          reg.remotenessChange = this.curBrowser.register(uid, message.target);
+        }
+        // set to true if we updated mainContentId
+        mainContent = ((mainContent == true) && (this.curBrowser.mainContentId != null));
+        if (mainContent) {
+          this.mainContentFrameId = this.curBrowser.curFrameId;
+        }
+        this.curBrowser.elementManager.seenItems[reg.id] = Cu.getWeakReference(listenerWindow);
+        if (nullPrevious && (this.curBrowser.curFrameId != null)) {
+          if (!this.sendAsync("newSession",
+              { B2G: (appName == "B2G"),
+                raisesAccessibilityExceptions:
+                  this.sessionCapabilities.raisesAccessibilityExceptions },
+              this.newSessionCommandId)) {
+            return;
+          }
+          if (this.curBrowser.newSession) {
+            this.getSessionCapabilities();
+            this.newSessionCommandId = null;
+          }
+        }
+        if (this.curBrowser.frameRegsPending) {
+          if (this.curBrowser.frameRegsPending > 0) {
+            this.curBrowser.frameRegsPending -= 1;
+          }
+          if (this.curBrowser.frameRegsPending === 0) {
+            // In case of a freshly registered window, we're responsible here
+            // for sending the ack.
+            this.sendOk(this.command_id);
+          }
+        }
+        return [reg, mainContent];
+      case "Marionette:emitTouchEvent":
+        let globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"]
+                             .getService(Ci.nsIMessageBroadcaster);
+        globalMessageManager.broadcastAsyncMessage(
+          "MarionetteMainListener:emitTouchEvent", message.json);
+        return;
+      case "Marionette:listenersAttached":
+        if (message.json.listenerId === this.curBrowser.curFrameId) {
+          // If remoteness gets updated we need to call newSession. In the case
+          // of desktop this just sets up a small amount of state that doesn't
+          // change over the course of a session.
+          let newSessionValues = {
+            B2G: (appName == "B2G"),
+            raisesAccessibilityExceptions: this.sessionCapabilities.raisesAccessibilityExceptions
+          };
+          this.sendAsync("newSession", newSessionValues);
+          this.curBrowser.flushPendingCommands();
+        }
+        return;
+    }
+  }
+};
+
+MarionetteServerConnection.prototype.requestTypes = {
+  "getMarionetteID": MarionetteServerConnection.prototype.getMarionetteID,
+  "sayHello": MarionetteServerConnection.prototype.sayHello,
+  "newSession": MarionetteServerConnection.prototype.newSession,
+  "getSessionCapabilities": MarionetteServerConnection.prototype.getSessionCapabilities,
+  "log": MarionetteServerConnection.prototype.log,
+  "getLogs": MarionetteServerConnection.prototype.getLogs,
+  "setContext": MarionetteServerConnection.prototype.setContext,
+  "getContext": MarionetteServerConnection.prototype.getContext,
+  "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,
+  "findChildElement": MarionetteServerConnection.prototype.findChildElements, // Needed for WebDriver compat
+  "findElements": MarionetteServerConnection.prototype.findElements,
+  "findChildElements":MarionetteServerConnection.prototype.findChildElements, // Needed for WebDriver compat
+  "clickElement": MarionetteServerConnection.prototype.clickElement,
+  "getElementAttribute": MarionetteServerConnection.prototype.getElementAttribute,
+  "getElementText": MarionetteServerConnection.prototype.getElementText,
+  "getElementTagName": MarionetteServerConnection.prototype.getElementTagName,
+  "isElementDisplayed": MarionetteServerConnection.prototype.isElementDisplayed,
+  "getElementValueOfCssProperty": MarionetteServerConnection.prototype.getElementValueOfCssProperty,
+  "submitElement": MarionetteServerConnection.prototype.submitElement,
+  "getElementSize": MarionetteServerConnection.prototype.getElementSize,  //deprecated
+  "getElementRect": MarionetteServerConnection.prototype.getElementRect,
+  "isElementEnabled": MarionetteServerConnection.prototype.isElementEnabled,
+  "isElementSelected": MarionetteServerConnection.prototype.isElementSelected,
+  "sendKeysToElement": MarionetteServerConnection.prototype.sendKeysToElement,
+  "getElementLocation": MarionetteServerConnection.prototype.getElementLocation,  // deprecated
+  "getElementPosition": MarionetteServerConnection.prototype.getElementLocation,  // deprecated
+  "clearElement": MarionetteServerConnection.prototype.clearElement,
+  "getTitle": MarionetteServerConnection.prototype.getTitle,
+  "getWindowType": MarionetteServerConnection.prototype.getWindowType,
+  "getPageSource": MarionetteServerConnection.prototype.getPageSource,
+  "get": MarionetteServerConnection.prototype.get,
+  "goUrl": MarionetteServerConnection.prototype.get,  // deprecated
+  "getCurrentUrl": MarionetteServerConnection.prototype.getCurrentUrl,
+  "getUrl": MarionetteServerConnection.prototype.getCurrentUrl,  // deprecated
+  "goBack": MarionetteServerConnection.prototype.goBack,
+  "goForward": MarionetteServerConnection.prototype.goForward,
+  "refresh":  MarionetteServerConnection.prototype.refresh,
+  "getWindowHandle": MarionetteServerConnection.prototype.getWindowHandle,
+  "getCurrentWindowHandle":  MarionetteServerConnection.prototype.getWindowHandle,  // Selenium 2 compat
+  "getChromeWindowHandle": MarionetteServerConnection.prototype.getChromeWindowHandle,
+  "getCurrentChromeWindowHandle": MarionetteServerConnection.prototype.getChromeWindowHandle,
+  "getWindow":  MarionetteServerConnection.prototype.getWindowHandle,  // deprecated
+  "getWindowHandles": MarionetteServerConnection.prototype.getWindowHandles,
+  "getChromeWindowHandles": MarionetteServerConnection.prototype.getChromeWindowHandles,
+  "getCurrentWindowHandles": MarionetteServerConnection.prototype.getWindowHandles,  // Selenium 2 compat
+  "getWindows":  MarionetteServerConnection.prototype.getWindowHandles,  // deprecated
+  "getWindowPosition": MarionetteServerConnection.prototype.getWindowPosition,
+  "setWindowPosition": MarionetteServerConnection.prototype.setWindowPosition,
+  "getActiveFrame": MarionetteServerConnection.prototype.getActiveFrame,
+  "switchToFrame": MarionetteServerConnection.prototype.switchToFrame,
+  "switchToWindow": MarionetteServerConnection.prototype.switchToWindow,
+  "deleteSession": MarionetteServerConnection.prototype.deleteSession,
+  "quitApplication": MarionetteServerConnection.prototype.quitApplication,
+  "emulatorCmdResult": MarionetteServerConnection.prototype.emulatorCmdResult,
+  "importScript": MarionetteServerConnection.prototype.importScript,
+  "clearImportedScripts": MarionetteServerConnection.prototype.clearImportedScripts,
+  "getAppCacheStatus": MarionetteServerConnection.prototype.getAppCacheStatus,
+  "close": MarionetteServerConnection.prototype.close,
+  "closeWindow": MarionetteServerConnection.prototype.close,  // deprecated
+  "closeChromeWindow": MarionetteServerConnection.prototype.closeChromeWindow,
+  "setTestName": MarionetteServerConnection.prototype.setTestName,
+  "takeScreenshot": MarionetteServerConnection.prototype.takeScreenshot,
+  "screenShot": MarionetteServerConnection.prototype.takeScreenshot,  // deprecated
+  "screenshot": MarionetteServerConnection.prototype.takeScreenshot,  // Selenium 2 compat
+  "addCookie": MarionetteServerConnection.prototype.addCookie,
+  "getCookies": MarionetteServerConnection.prototype.getCookies,
+  "getAllCookies": MarionetteServerConnection.prototype.getCookies,  // deprecated
+  "deleteAllCookies": MarionetteServerConnection.prototype.deleteAllCookies,
+  "deleteCookie": MarionetteServerConnection.prototype.deleteCookie,
+  "getActiveElement": MarionetteServerConnection.prototype.getActiveElement,
+  "getScreenOrientation": MarionetteServerConnection.prototype.getScreenOrientation,
+  "setScreenOrientation": MarionetteServerConnection.prototype.setScreenOrientation,
+  "getWindowSize": MarionetteServerConnection.prototype.getWindowSize,
+  "setWindowSize": MarionetteServerConnection.prototype.setWindowSize,
+  "maximizeWindow": MarionetteServerConnection.prototype.maximizeWindow,
+  "dismissDialog": MarionetteServerConnection.prototype.dismissDialog,
+  "acceptDialog": MarionetteServerConnection.prototype.acceptDialog,
+  "getTextFromDialog": MarionetteServerConnection.prototype.getTextFromDialog,
+  "sendKeysToDialog": MarionetteServerConnection.prototype.sendKeysToDialog
+};
+
+/**
+ * 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
+ */
+
+function BrowserObj(win, server) {
+  this.DESKTOP = "desktop";
+  this.B2G = "B2G";
+  this.browser;
+  this.window = win;
+  this.knownFrames = [];
+  this.curFrameId = null;
+  this.startPage = "about:blank";
+  this.mainContentId = null; // used in B2G to identify the homescreen content page
+  this.newSession = true; //used to set curFrameId upon new session
+  this.elementManager = new ElementManager([NAME, LINK_TEXT, PARTIAL_LINK_TEXT]);
+  this.setBrowser(win);
+  this.frameManager = new FrameManager(server); //We should have one FM per BO so that we can handle modals in each Browser
+
+  // A reference to the tab corresponding to the current window handle, if any.
+  this.tab = null;
+  this.pendingCommands = [];
+
+  //register all message listeners
+  this.frameManager.addMessageManagerListeners(server.messageManager);
+  this.getIdForBrowser = server.getIdForBrowser.bind(server);
+  this.updateIdForBrowser = server.updateIdForBrowser.bind(server);
+  this._curFrameId = null;
+  this._browserWasRemote = null;
+  this._hasRemotenessChange = false;
+}
+
+BrowserObj.prototype = {
+
+  /**
+   * This function intercepts commands interacting with content and queues
+   * or executes them as needed.
+   *
+   * No commands interacting with content are safe to process until
+   * the new listener script is loaded and registers itself.
+   * This occurs when a command whose effect is asynchronous (such
+   * as goBack) results in a remoteness change and new commands
+   * are subsequently posted to the server.
+   */
+  executeWhenReady: function (callback) {
+    if (this.hasRemotenessChange()) {
+      this.pendingCommands.push(callback);