Bug 776875 - GCLI: Move existing GCLI commands into JSMs; r=jwalker
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Tue, 14 Aug 2012 15:51:48 +0100
changeset 105422 ebb525cd2e7018a51b30c4a687fd66505f471828
parent 105421 f4596ef17eed20290eb9b62fb2089272544d4ac9
child 105423 efd1509146d56880a49c04dbac6c937fbb089fab
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersjwalker
bugs776875
milestone17.0a1
Bug 776875 - GCLI: Move existing GCLI commands into JSMs; r=jwalker
browser/devtools/commandline/CmdAddon.jsm
browser/devtools/commandline/CmdBreak.jsm
browser/devtools/commandline/CmdCalllog.jsm
browser/devtools/commandline/CmdCmd.jsm
browser/devtools/commandline/CmdConsole.jsm
browser/devtools/commandline/CmdCookie.jsm
browser/devtools/commandline/CmdDbg.jsm
browser/devtools/commandline/CmdEcho.jsm
browser/devtools/commandline/CmdExport.jsm
browser/devtools/commandline/CmdJsb.jsm
browser/devtools/commandline/CmdPagemod.jsm
browser/devtools/commandline/CmdRestart.jsm
browser/devtools/commandline/CmdScreenshot.jsm
browser/devtools/commandline/Commands.jsm
browser/devtools/commandline/GcliCommands.jsm
browser/devtools/commandline/GcliCookieCommands.jsm
browser/devtools/commandline/GcliTiltCommands.jsm
browser/devtools/commandline/commandline.css
browser/devtools/commandline/commandlineoutput.xhtml
browser/devtools/commandline/commandlinetooltip.xhtml
browser/devtools/commandline/gcli.css
browser/devtools/commandline/gclioutput.xhtml
browser/devtools/commandline/gclitooltip.xhtml
browser/devtools/commandline/test/Makefile.in
browser/devtools/commandline/test/browser_cmd_addon.js
browser/devtools/commandline/test/browser_cmd_calllog.js
browser/devtools/commandline/test/browser_cmd_commands.js
browser/devtools/commandline/test/browser_cmd_cookie.js
browser/devtools/commandline/test/browser_cmd_integrate.js
browser/devtools/commandline/test/browser_cmd_jsb.js
browser/devtools/commandline/test/browser_cmd_jsb_script.jsi
browser/devtools/commandline/test/browser_cmd_pagemod_export.html
browser/devtools/commandline/test/browser_cmd_pagemod_export.js
browser/devtools/commandline/test/browser_cmd_pref.js
browser/devtools/commandline/test/browser_cmd_restart.js
browser/devtools/commandline/test/browser_cmd_settings.js
browser/devtools/commandline/test/browser_dbg_cmd.html
browser/devtools/commandline/test/browser_dbg_cmd.js
browser/devtools/commandline/test/browser_dbg_cmd_break.html
browser/devtools/commandline/test/browser_dbg_cmd_break.js
browser/devtools/commandline/test/browser_gcli_addon.js
browser/devtools/commandline/test/browser_gcli_break.html
browser/devtools/commandline/test/browser_gcli_break.js
browser/devtools/commandline/test/browser_gcli_calllog.js
browser/devtools/commandline/test/browser_gcli_commands.js
browser/devtools/commandline/test/browser_gcli_cookie.js
browser/devtools/commandline/test/browser_gcli_dbg.js
browser/devtools/commandline/test/browser_gcli_edit.js
browser/devtools/commandline/test/browser_gcli_inspect.html
browser/devtools/commandline/test/browser_gcli_inspect.js
browser/devtools/commandline/test/browser_gcli_integrate.js
browser/devtools/commandline/test/browser_gcli_jsb.js
browser/devtools/commandline/test/browser_gcli_pagemod_export.js
browser/devtools/commandline/test/browser_gcli_pref.js
browser/devtools/commandline/test/browser_gcli_responsivemode.js
browser/devtools/commandline/test/browser_gcli_restart.js
browser/devtools/commandline/test/browser_gcli_settings.js
browser/devtools/commandline/test/head.js
browser/devtools/commandline/test/helper.js
browser/devtools/commandline/test/resources.html
browser/devtools/commandline/test/resources_dbg.html
browser/devtools/commandline/test/resources_inpage.js
browser/devtools/commandline/test/resources_inpage1.css
browser/devtools/commandline/test/resources_inpage2.css
browser/devtools/commandline/test/resources_jsb_script.js
browser/devtools/highlighter/CmdInspect.jsm
browser/devtools/highlighter/Makefile.in
browser/devtools/highlighter/test/Makefile.in
browser/devtools/highlighter/test/browser_inspector_cmd_inspect.html
browser/devtools/highlighter/test/browser_inspector_cmd_inspect.js
browser/devtools/highlighter/test/head.js
browser/devtools/highlighter/test/helper.js
browser/devtools/jar.mn
browser/devtools/responsivedesign/CmdResize.jsm
browser/devtools/responsivedesign/test/Makefile.in
browser/devtools/responsivedesign/test/browser_responsive_cmd.js
browser/devtools/responsivedesign/test/head.js
browser/devtools/responsivedesign/test/helper.js
browser/devtools/shared/DeveloperToolbar.jsm
browser/devtools/shared/test/Makefile.in
browser/devtools/shared/test/head.js
browser/devtools/shared/test/helper.js
browser/devtools/shared/test/leakhunt.js
browser/devtools/styleeditor/CmdEdit.jsm
browser/devtools/styleeditor/test/Makefile.in
browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.html
browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.js
browser/devtools/styleeditor/test/head.js
browser/devtools/styleeditor/test/helper.js
browser/devtools/styleeditor/test/resources_inpage.jsi
browser/devtools/styleeditor/test/resources_inpage1.css
browser/devtools/styleeditor/test/resources_inpage2.css
browser/devtools/tilt/CmdTilt.jsm
browser/themes/gnomestripe/devtools/commandline.css
browser/themes/gnomestripe/devtools/gcli.css
browser/themes/gnomestripe/jar.mn
browser/themes/pinstripe/devtools/commandline.css
browser/themes/pinstripe/devtools/gcli.css
browser/themes/pinstripe/jar.mn
browser/themes/winstripe/devtools/commandline.css
browser/themes/winstripe/devtools/gcli.css
browser/themes/winstripe/jar.mn
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdAddon.jsm
@@ -0,0 +1,290 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
+                                  "resource://gre/modules/AddonManager.jsm");
+
+/**
+ * 'addon' command.
+ */
+gcli.addCommand({
+  name: "addon",
+  description: gcli.lookup("addonDesc")
+});
+
+/**
+ * 'addon list' command.
+ */
+gcli.addCommand({
+  name: "addon list",
+  description: gcli.lookup("addonListDesc"),
+  params: [{
+    name: 'type',
+    type: {
+      name: 'selection',
+      data: ["dictionary", "extension", "locale", "plugin", "theme", "all"]
+    },
+    defaultValue: 'all',
+    description: gcli.lookup("addonListTypeDesc"),
+  }],
+  exec: function(aArgs, context) {
+    function representEnabledAddon(aAddon) {
+      return "<li><![CDATA[" + aAddon.name + "\u2002" + aAddon.version +
+      getAddonStatus(aAddon) + "]]></li>";
+    }
+
+    function representDisabledAddon(aAddon) {
+      return "<li class=\"gcli-addon-disabled\">" +
+        "<![CDATA[" + aAddon.name + "\u2002" + aAddon.version + aAddon.version +
+        "]]></li>";
+    }
+
+    function getAddonStatus(aAddon) {
+      let operations = [];
+
+      if (aAddon.pendingOperations & AddonManager.PENDING_ENABLE) {
+        operations.push("PENDING_ENABLE");
+      }
+
+      if (aAddon.pendingOperations & AddonManager.PENDING_DISABLE) {
+        operations.push("PENDING_DISABLE");
+      }
+
+      if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
+        operations.push("PENDING_UNINSTALL");
+      }
+
+      if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL) {
+        operations.push("PENDING_INSTALL");
+      }
+
+      if (aAddon.pendingOperations & AddonManager.PENDING_UPGRADE) {
+        operations.push("PENDING_UPGRADE");
+      }
+
+      if (operations.length) {
+        return " (" + operations.join(", ") + ")";
+      }
+      return "";
+    }
+
+    /**
+     * Compares two addons by their name. Used in sorting.
+     */
+    function compareAddonNames(aNameA, aNameB) {
+      return String.localeCompare(aNameA.name, aNameB.name);
+    }
+
+    /**
+     * Resolves the promise which is the scope (this) of this function, filling
+     * it with an HTML representation of the passed add-ons.
+     */
+    function list(aType, aAddons) {
+      if (!aAddons.length) {
+        this.resolve(gcli.lookup("addonNoneOfType"));
+      }
+
+      // Separate the enabled add-ons from the disabled ones.
+      let enabledAddons = [];
+      let disabledAddons = [];
+
+      aAddons.forEach(function(aAddon) {
+        if (aAddon.isActive) {
+          enabledAddons.push(aAddon);
+        } else {
+          disabledAddons.push(aAddon);
+        }
+      });
+
+      let header;
+      switch(aType) {
+        case "dictionary":
+          header = gcli.lookup("addonListDictionaryHeading");
+          break;
+        case "extension":
+          header = gcli.lookup("addonListExtensionHeading");
+          break;
+        case "locale":
+          header = gcli.lookup("addonListLocaleHeading");
+          break;
+        case "plugin":
+          header = gcli.lookup("addonListPluginHeading");
+          break;
+        case "theme":
+          header = gcli.lookup("addonListThemeHeading");
+        case "all":
+          header = gcli.lookup("addonListAllHeading");
+          break;
+        default:
+          header = gcli.lookup("addonListUnknownHeading");
+      }
+
+      // Map and sort the add-ons, and create an HTML list.
+      this.resolve(header +
+        "<ol>" +
+        enabledAddons.sort(compareAddonNames).map(representEnabledAddon).join("") +
+        disabledAddons.sort(compareAddonNames).map(representDisabledAddon).join("") +
+        "</ol>");
+    }
+
+    // Create the promise that will be resolved when the add-on listing has
+    // been finished.
+    let promise = context.createPromise();
+    let types = aArgs.type == "all" ? null : [aArgs.type];
+    AddonManager.getAddonsByTypes(types, list.bind(promise, aArgs.type));
+    return promise;
+  }
+});
+
+// We need a list of addon names for the enable and disable commands. Because
+// getting the name list is async we do not add the commands until we have the
+// list.
+AddonManager.getAllAddons(function addonAsync(aAddons) {
+  // We listen for installs to keep our addon list up to date. There is no need
+  // to listen for uninstalls because uninstalled addons are simply disabled
+  // until restart (to enable undo functionality).
+  AddonManager.addAddonListener({
+    onInstalled: function(aAddon) {
+      addonNameCache.push({
+        name: representAddon(aAddon).replace(/\s/g, "_"),
+        value: aAddon.name
+      });
+    },
+    onUninstalled: function(aAddon) {
+      let name = representAddon(aAddon).replace(/\s/g, "_");
+
+      for (let i = 0; i < addonNameCache.length; i++) {
+        if(addonNameCache[i].name == name) {
+          addonNameCache.splice(i, 1);
+          break;
+        }
+      }
+    },
+  });
+
+  /**
+   * Returns a string that represents the passed add-on.
+   */
+  function representAddon(aAddon) {
+    let name = aAddon.name + " " + aAddon.version;
+    return name.trim();
+  }
+
+  let addonNameCache = [];
+
+  // The name parameter, used in "addon enable" and "addon disable."
+  let nameParameter = {
+    name: "name",
+    type: {
+      name: "selection",
+      lookup: addonNameCache
+    },
+    description: gcli.lookup("addonNameDesc")
+  };
+
+  for (let addon of aAddons) {
+    addonNameCache.push({
+      name: representAddon(addon).replace(/\s/g, "_"),
+      value: addon.name
+    });
+  }
+
+  /**
+   * 'addon enable' command.
+   */
+  gcli.addCommand({
+    name: "addon enable",
+    description: gcli.lookup("addonEnableDesc"),
+    params: [nameParameter],
+    exec: function(aArgs, context) {
+      /**
+       * Enables the addon in the passed list which has a name that matches
+       * according to the passed name comparer, and resolves the promise which
+       * is the scope (this) of this function to display the result of this
+       * enable attempt.
+       */
+      function enable(aName, addons) {
+        // Find the add-on.
+        let addon = null;
+        addons.some(function(candidate) {
+          if (candidate.name == aName) {
+            addon = candidate;
+            return true;
+          } else {
+            return false;
+          }
+        });
+
+        let name = representAddon(addon);
+
+        if (!addon.userDisabled) {
+          this.resolve("<![CDATA[" +
+            gcli.lookupFormat("addonAlreadyEnabled", [name]) + "]]>");
+        } else {
+          addon.userDisabled = false;
+          // nl-nl: {$1} is ingeschakeld.
+          this.resolve("<![CDATA[" +
+            gcli.lookupFormat("addonEnabled", [name]) + "]]>");
+        }
+      }
+
+      let promise = context.createPromise();
+      // List the installed add-ons, enable one when done listing.
+      AddonManager.getAllAddons(enable.bind(promise, aArgs.name));
+      return promise;
+    }
+  });
+
+  /**
+   * 'addon disable' command.
+   */
+  gcli.addCommand({
+    name: "addon disable",
+    description: gcli.lookup("addonDisableDesc"),
+    params: [nameParameter],
+    exec: function(aArgs, context) {
+      /**
+       * Like enable, but ... you know ... the exact opposite.
+       */
+      function disable(aName, addons) {
+        // Find the add-on.
+        let addon = null;
+        addons.some(function(candidate) {
+          if (candidate.name == aName) {
+            addon = candidate;
+            return true;
+          } else {
+            return false;
+          }
+        });
+
+        let name = representAddon(addon);
+
+        if (addon.userDisabled) {
+          this.resolve("<![CDATA[" +
+            gcli.lookupFormat("addonAlreadyDisabled", [name]) + "]]>");
+        } else {
+          addon.userDisabled = true;
+          // nl-nl: {$1} is uitgeschakeld.
+          this.resolve("<![CDATA[" +
+            gcli.lookupFormat("addonDisabled", [name]) + "]]>");
+        }
+      }
+
+      let promise = context.createPromise();
+      // List the installed add-ons, disable one when done listing.
+      AddonManager.getAllAddons(disable.bind(promise, aArgs.name));
+      return promise;
+    }
+  });
+  Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null);
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdBreak.jsm
@@ -0,0 +1,170 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
+                                  "resource:///modules/HUDService.jsm");
+
+/**
+ * 'break' command
+ */
+gcli.addCommand({
+  name: "break",
+  description: gcli.lookup("breakDesc"),
+  manual: gcli.lookup("breakManual")
+});
+
+
+/**
+ * 'break list' command
+ */
+gcli.addCommand({
+  name: "break list",
+  description: gcli.lookup("breaklistDesc"),
+  returnType: "html",
+  exec: function(args, context) {
+    let win = HUDService.currentContext();
+    let dbg = win.DebuggerUI.getDebugger();
+    if (!dbg) {
+      return gcli.lookup("breakaddDebuggerStopped");
+    }
+    let breakpoints = dbg.breakpoints;
+
+    if (Object.keys(breakpoints).length === 0) {
+      return gcli.lookup("breaklistNone");
+    }
+
+    let reply = gcli.lookup("breaklistIntro");
+    reply += "<ol>";
+    for each (let breakpoint in breakpoints) {
+      let text = gcli.lookupFormat("breaklistLineEntry",
+                                   [breakpoint.location.url,
+                                    breakpoint.location.line]);
+      reply += "<li>" + text + "</li>";
+    };
+    reply += "</ol>";
+    return reply;
+  }
+});
+
+
+/**
+ * 'break add' command
+ */
+gcli.addCommand({
+  name: "break add",
+  description: gcli.lookup("breakaddDesc"),
+  manual: gcli.lookup("breakaddManual")
+});
+
+/**
+ * 'break add line' command
+ */
+gcli.addCommand({
+  name: "break add line",
+  description: gcli.lookup("breakaddlineDesc"),
+  params: [
+    {
+      name: "file",
+      type: {
+        name: "selection",
+        data: function() {
+          let win = HUDService.currentContext();
+          let dbg = win.DebuggerUI.getDebugger();
+          let files = [];
+          if (dbg) {
+            let scriptsView = dbg.contentWindow.DebuggerView.Scripts;
+            for each (let script in scriptsView.scriptLocations) {
+              files.push(script);
+            }
+          }
+          return files;
+        }
+      },
+      description: gcli.lookup("breakaddlineFileDesc")
+    },
+    {
+      name: "line",
+      type: { name: "number", min: 1, step: 10 },
+      description: gcli.lookup("breakaddlineLineDesc")
+    }
+  ],
+  returnType: "html",
+  exec: function(args, context) {
+    args.type = "line";
+    let win = HUDService.currentContext();
+    let dbg = win.DebuggerUI.getDebugger();
+    if (!dbg) {
+      return gcli.lookup("breakaddDebuggerStopped");
+    }
+    var promise = context.createPromise();
+    let position = { url: args.file, line: args.line };
+    dbg.addBreakpoint(position, function(aBreakpoint, aError) {
+      if (aError) {
+        promise.resolve(gcli.lookupFormat("breakaddFailed", [aError]));
+        return;
+      }
+      promise.resolve(gcli.lookup("breakaddAdded"));
+    });
+    return promise;
+  }
+});
+
+
+/**
+ * 'break del' command
+ */
+gcli.addCommand({
+  name: "break del",
+  description: gcli.lookup("breakdelDesc"),
+  params: [
+    {
+      name: "breakid",
+      type: {
+        name: "number",
+        min: 0,
+        max: function() {
+          let win = HUDService.currentContext();
+          let dbg = win.DebuggerUI.getDebugger();
+          if (!dbg) {
+            return gcli.lookup("breakaddDebuggerStopped");
+          }
+          return Object.keys(dbg.breakpoints).length - 1;
+        },
+      },
+      description: gcli.lookup("breakdelBreakidDesc")
+    }
+  ],
+  returnType: "html",
+  exec: function(args, context) {
+    let win = HUDService.currentContext();
+    let dbg = win.DebuggerUI.getDebugger();
+    if (!dbg) {
+      return gcli.lookup("breakaddDebuggerStopped");
+    }
+
+    let breakpoints = dbg.breakpoints;
+    let id = Object.keys(dbg.breakpoints)[args.breakid];
+    if (!id || !(id in breakpoints)) {
+      return gcli.lookup("breakNotFound");
+    }
+
+    let promise = context.createPromise();
+    try {
+      dbg.removeBreakpoint(breakpoints[id], function() {
+        promise.resolve(gcli.lookup("breakdelRemoved"));
+      });
+    } catch (ex) {
+      // If the debugger has been closed already, don't scare the user.
+      promise.resolve(gcli.lookup("breakdelRemoved"));
+    }
+    return promise;
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdCalllog.jsm
@@ -0,0 +1,103 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
+                                  "resource:///modules/HUDService.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "Debugger", function() {
+  let JsDebugger = {};
+  Components.utils.import("resource://gre/modules/jsdebugger.jsm", JsDebugger);
+
+  let global = Components.utils.getGlobalForObject({});
+  JsDebugger.addDebuggerToGlobal(global);
+
+  return global.Debugger;
+});
+
+let debuggers = [];
+
+/**
+ * 'calllog' command
+ */
+gcli.addCommand({
+  name: "calllog",
+  description: gcli.lookup("calllogDesc")
+})
+
+/**
+ * 'calllog start' command
+ */
+gcli.addCommand({
+  name: "calllog start",
+  description: gcli.lookup("calllogStartDesc"),
+
+  exec: function(args, context) {
+    let contentWindow = context.environment.contentDocument.defaultView;
+
+    let dbg = new Debugger(contentWindow);
+    dbg.onEnterFrame = function(frame) {
+      // BUG 773652 -  Make the output from the GCLI calllog command nicer
+      contentWindow.console.log("Method call: " + this.callDescription(frame));
+    }.bind(this);
+
+    debuggers.push(dbg);
+
+    let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab;
+    HUDService.activateHUDForContext(tab);
+
+    return gcli.lookup("calllogStartReply");
+  },
+
+  callDescription: function(frame) {
+    let name = "<anonymous>";
+    if (frame.callee.name) {
+      name = frame.callee.name;
+    }
+    else {
+      let desc = frame.callee.getOwnPropertyDescriptor("displayName");
+      if (desc && desc.value && typeof desc.value == "string") {
+        name = desc.value;
+      }
+    }
+
+    let args = frame.arguments.map(this.valueToString).join(", ");
+    return name + "(" + args + ")";
+  },
+
+  valueToString: function(value) {
+    if (typeof value !== "object" || value === null) {
+      return uneval(value);
+    }
+    return "[object " + value.class + "]";
+  }
+});
+
+/**
+ * 'calllog stop' command
+ */
+gcli.addCommand({
+  name: "calllog stop",
+  description: gcli.lookup("calllogStopDesc"),
+
+  exec: function(args, context) {
+    let numDebuggers = debuggers.length;
+    if (numDebuggers == 0) {
+      return gcli.lookup("calllogStopNoLogging");
+    }
+
+    for (let dbg of debuggers) {
+      dbg.onEnterFrame = undefined;
+    }
+    debuggers = [];
+
+    return gcli.lookupFormat("calllogStopReply", [ numDebuggers ]);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdCmd.jsm
@@ -0,0 +1,126 @@
+/* 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 EXPORTED_SYMBOLS = [ "CmdCommands" ];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+let prefSvc = "@mozilla.org/preferences-service;1";
+XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
+  let prefService = Cc[prefSvc].getService(Ci.nsIPrefService);
+  return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
+});
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+                                  "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "console",
+                                  "resource:///modules/devtools/Console.jsm");
+
+/**
+ * A place to store the names of the commands that we have added as a result of
+ * calling refreshAutoCommands(). Used by refreshAutoCommands to remove the
+ * added commands.
+ */
+let commands = [];
+
+/**
+ * Exported API
+ */
+let CmdCommands = {
+  /**
+   * Called to look in a directory pointed at by the devtools.commands.dir pref
+   * for *.mozcmd files which are then loaded.
+   * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which
+   * we eval the script from the .mozcmd file. This should be a chrome window.
+   */
+  refreshAutoCommands: function GC_refreshAutoCommands(aSandboxPrincipal) {
+    // First get rid of the last set of commands
+    commands.forEach(function(name) {
+      gcli.removeCommand(name);
+    });
+
+    let dirName = prefBranch.getComplexValue("devtools.commands.dir",
+                                             Ci.nsISupportsString).data;
+    if (dirName == "") {
+      return;
+    }
+
+    let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+    dir.initWithPath(dirName);
+    if (!dir.exists() || !dir.isDirectory()) {
+      throw new Error('\'' + dirName + '\' is not a directory.');
+    }
+
+    let en = dir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
+
+    while (true) {
+      let file = en.nextFile;
+      if (!file) {
+        break;
+      }
+      if (file.leafName.match(/.*\.mozcmd$/) && file.isFile() && file.isReadable()) {
+        loadCommandFile(file, aSandboxPrincipal);
+      }
+    }
+  },
+};
+
+/**
+ * Load the commands from a single file
+ * @param nsIFile aFile The file containing the commands that we should read
+ * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which
+ * we eval the script from the .mozcmd file. This should be a chrome window.
+ */
+function loadCommandFile(aFile, aSandboxPrincipal) {
+  NetUtil.asyncFetch(aFile, function refresh_fetch(aStream, aStatus) {
+    if (!Components.isSuccessCode(aStatus)) {
+      console.error("NetUtil.asyncFetch(" + aFile.path + ",..) failed. Status=" + aStatus);
+      return;
+    }
+
+    let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+    aStream.close();
+
+    let sandbox = new Cu.Sandbox(aSandboxPrincipal, {
+      sandboxPrototype: aSandboxPrincipal,
+      wantXrays: false,
+      sandboxName: aFile.path
+    });
+    let data = Cu.evalInSandbox(source, sandbox, "1.8", aFile.leafName, 1);
+
+    if (!Array.isArray(data)) {
+      console.error("Command file '" + aFile.leafName + "' does not have top level array.");
+      return;
+    }
+
+    data.forEach(function(commandSpec) {
+      gcli.addCommand(commandSpec);
+      commands.push(commandSpec.name);
+    });
+  }.bind(this));
+}
+
+/**
+ * 'cmd' command
+ */
+gcli.addCommand({
+  name: "cmd",
+  description: gcli.lookup("cmdDesc"),
+  hidden: true
+});
+
+/**
+ * 'cmd refresh' command
+ */
+gcli.addCommand({
+  name: "cmd refresh",
+  description: gcli.lookup("cmdRefreshDesc"),
+  hidden: true,
+  exec: function Command_cmdRefresh(args, context) {
+    GcliCmdCommands.refreshAutoCommands(context.environment.chromeDocument.defaultView);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdConsole.jsm
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
+                                  "resource:///modules/HUDService.jsm");
+
+/**
+ * 'console' command
+ */
+gcli.addCommand({
+  name: "console",
+  description: gcli.lookup("consoleDesc"),
+  manual: gcli.lookup("consoleManual")
+});
+
+/**
+ * 'console clear' command
+ */
+gcli.addCommand({
+  name: "console clear",
+  description: gcli.lookup("consoleclearDesc"),
+  exec: function Command_consoleClear(args, context) {
+    let window = context.environment.contentDocument.defaultView;
+    let hud = HUDService.getHudByWindow(window);
+    // hud will be null if the web console has not been opened for this window
+    if (hud) {
+      hud.jsterm.clearOutput();
+    }
+  }
+});
+
+/**
+ * 'console close' command
+ */
+gcli.addCommand({
+  name: "console close",
+  description: gcli.lookup("consolecloseDesc"),
+  exec: function Command_consoleClose(args, context) {
+    let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab
+    HUDService.deactivateHUDForContext(tab);
+  }
+});
+
+/**
+ * 'console open' command
+ */
+gcli.addCommand({
+  name: "console open",
+  description: gcli.lookup("consoleopenDesc"),
+  exec: function Command_consoleOpen(args, context) {
+    let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab
+    HUDService.activateHUDForContext(tab);
+  }
+});
rename from browser/devtools/commandline/GcliCookieCommands.jsm
rename to browser/devtools/commandline/CmdCookie.jsm
--- a/browser/devtools/commandline/GcliCookieCommands.jsm
+++ b/browser/devtools/commandline/CmdCookie.jsm
@@ -1,17 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 let EXPORTED_SYMBOLS = [ ];
 
-Components.utils.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "console",
+                                  "resource:///modules/devtools/Console.jsm");
 
 // We should really be using nsICookieManager so we can read more than just the
 // key/value of cookies. The difficulty is filtering the cookies that are
 // relevant to the current page. See
 // https://github.com/firebug/firebug/blob/master/extension/content/firebug/cookies/cookieObserver.js#L123
 // For details on how this is done with Firebug
 
 /**
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdDbg.jsm
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+/**
+ * 'dbg' command
+ */
+gcli.addCommand({
+  name: "dbg",
+  description: gcli.lookup("dbgDesc"),
+  manual: gcli.lookup("dbgManual")
+});
+
+
+/**
+ * 'dbg interrupt' command
+ */
+gcli.addCommand({
+  name: "dbg interrupt",
+  description: gcli.lookup("dbgInterrupt"),
+  params: [],
+  exec: function(args, context) {
+    let win = context.environment.chromeDocument.defaultView;
+    let dbg = win.DebuggerUI.getDebugger();
+
+    if (dbg) {
+      let controller = dbg.contentWindow.DebuggerController;
+      let thread = controller.activeThread;
+      if (!thread.paused) {
+        thread.interrupt();
+      }
+    }
+  }
+});
+
+/**
+ * 'dbg continue' command
+ */
+gcli.addCommand({
+  name: "dbg continue",
+  description: gcli.lookup("dbgContinue"),
+  params: [],
+  exec: function(args, context) {
+    let win = context.environment.chromeDocument.defaultView;
+    let dbg = win.DebuggerUI.getDebugger();
+
+    if (dbg) {
+      let controller = dbg.contentWindow.DebuggerController;
+      let thread = controller.activeThread;
+      if (thread.paused) {
+        thread.resume();
+      }
+    }
+  }
+});
+
+
+/**
+ * 'dbg step' command
+ */
+gcli.addCommand({
+  name: "dbg step",
+  description: gcli.lookup("dbgStepDesc"),
+  manual: gcli.lookup("dbgStepManual")
+});
+
+
+/**
+ * 'dbg step over' command
+ */
+gcli.addCommand({
+  name: "dbg step over",
+  description: gcli.lookup("dbgStepOverDesc"),
+  params: [],
+  exec: function(args, context) {
+    let win = context.environment.chromeDocument.defaultView;
+    let dbg = win.DebuggerUI.getDebugger();
+
+    if (dbg) {
+      let controller = dbg.contentWindow.DebuggerController;
+      let thread = controller.activeThread;
+      if (thread.paused) {
+        thread.stepOver();
+      }
+    }
+  }
+});
+
+/**
+ * 'dbg step in' command
+ */
+gcli.addCommand({
+  name: 'dbg step in',
+  description: gcli.lookup("dbgStepInDesc"),
+  params: [],
+  exec: function(args, context) {
+    let win = context.environment.chromeDocument.defaultView;
+    let dbg = win.DebuggerUI.getDebugger();
+
+    if (dbg) {
+      let controller = dbg.contentWindow.DebuggerController;
+      let thread = controller.activeThread;
+      if (thread.paused) {
+        thread.stepIn();
+      }
+    }
+  }
+});
+
+/**
+ * 'dbg step over' command
+ */
+gcli.addCommand({
+  name: 'dbg step out',
+  description: gcli.lookup("dbgStepOutDesc"),
+  params: [],
+  exec: function(args, context) {
+    let win = context.environment.chromeDocument.defaultView;
+    let dbg = win.DebuggerUI.getDebugger();
+
+    if (dbg) {
+      let controller = dbg.contentWindow.DebuggerController;
+      let thread = controller.activeThread;
+      if (thread.paused) {
+        thread.stepOut();
+      }
+    }
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdEcho.jsm
@@ -0,0 +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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+
+/**
+ * 'echo' command
+ */
+gcli.addCommand({
+  name: "echo",
+  description: gcli.lookup("echoDesc"),
+  params: [
+    {
+      name: "message",
+      type: "string",
+      description: gcli.lookup("echoMessageDesc")
+    }
+  ],
+  returnType: "string",
+  hidden: true,
+  exec: function Command_echo(args, context) {
+    return args.message;
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdExport.jsm
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+
+/**
+ * 'export' command
+ */
+gcli.addCommand({
+  name: "export",
+  description: gcli.lookup("exportDesc"),
+});
+
+/**
+ * The 'export html' command. This command allows the user to export the page to
+ * HTML after they do DOM changes.
+ */
+gcli.addCommand({
+  name: "export html",
+  description: gcli.lookup("exportHtmlDesc"),
+  exec: function(args, context) {
+    let document = context.environment.contentDocument;
+    let window = document.defaultView;
+    let page = document.documentElement.outerHTML;
+    window.open('data:text/plain;charset=utf8,' + encodeURIComponent(page));
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdJsb.jsm
@@ -0,0 +1,138 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+const XMLHttpRequest =
+  Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/devtools/gcli.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "js_beautify",
+                                  "resource:///modules/devtools/Jsbeautify.jsm");
+
+/**
+ * jsb command.
+ */
+gcli.addCommand({
+  name: 'jsb',
+  description: gcli.lookup('jsbDesc'),
+  returnValue:'string',
+  hidden: true,
+  params: [
+    {
+      name: 'url',
+      type: 'string',
+      description: gcli.lookup('jsbUrlDesc'),
+      manual: 'The URL of the JS to prettify'
+    },
+    {
+      name: 'indentSize',
+      type: 'number',
+      description: gcli.lookup('jsbIndentSizeDesc'),
+      manual: gcli.lookup('jsbIndentSizeManual'),
+      defaultValue: 2
+    },
+    {
+      name: 'indentChar',
+      type: {
+        name: 'selection',
+        lookup: [{name: "space", value: " "}, {name: "tab", value: "\t"}]
+      },
+      description: gcli.lookup('jsbIndentCharDesc'),
+      manual: gcli.lookup('jsbIndentCharManual'),
+      defaultValue: ' ',
+    },
+    {
+      name: 'preserveNewlines',
+      type: 'boolean',
+      description: gcli.lookup('jsbPreserveNewlinesDesc'),
+      manual: gcli.lookup('jsbPreserveNewlinesManual'),
+      defaultValue: true
+    },
+    {
+      name: 'preserveMaxNewlines',
+      type: 'number',
+      description: gcli.lookup('jsbPreserveMaxNewlinesDesc'),
+      manual: gcli.lookup('jsbPreserveMaxNewlinesManual'),
+      defaultValue: -1
+    },
+    {
+      name: 'jslintHappy',
+      type: 'boolean',
+      description: gcli.lookup('jsbJslintHappyDesc'),
+      manual: gcli.lookup('jsbJslintHappyManual'),
+      defaultValue: false
+    },
+    {
+      name: 'braceStyle',
+      type: {
+        name: 'selection',
+        data: ['collapse', 'expand', 'end-expand', 'expand-strict']
+      },
+      description: gcli.lookup('jsbBraceStyleDesc'),
+      manual: gcli.lookup('jsbBraceStyleManual'),
+      defaultValue: "collapse"
+    },
+    {
+      name: 'spaceBeforeConditional',
+      type: 'boolean',
+      description: gcli.lookup('jsbSpaceBeforeConditionalDesc'),
+      manual: gcli.lookup('jsbSpaceBeforeConditionalManual'),
+      defaultValue: true
+    },
+    {
+      name: 'unescapeStrings',
+      type: 'boolean',
+      description: gcli.lookup('jsbUnescapeStringsDesc'),
+      manual: gcli.lookup('jsbUnescapeStringsManual'),
+      defaultValue: false
+    }
+  ],
+  exec: function(args, context) {
+  let opts = {
+    indent_size: args.indentSize,
+    indent_char: args.indentChar,
+    preserve_newlines: args.preserveNewlines,
+    max_preserve_newlines: args.preserveMaxNewlines == -1 ?
+                           undefined : args.preserveMaxNewlines,
+    jslint_happy: args.jslintHappy,
+    brace_style: args.braceStyle,
+    space_before_conditional: args.spaceBeforeConditional,
+    unescape_strings: args.unescapeStrings
+  }
+
+  let xhr = new XMLHttpRequest();
+
+  try {
+    xhr.open("GET", args.url, true);
+  } catch(e) {
+    return gcli.lookup('jsbInvalidURL');
+  }
+
+  let promise = context.createPromise();
+
+  xhr.onreadystatechange = function(aEvt) {
+    if (xhr.readyState == 4) {
+      if (xhr.status == 200 || xhr.status == 0) {
+        let browserDoc = context.environment.chromeDocument;
+        let browserWindow = browserDoc.defaultView;
+        let browser = browserWindow.gBrowser;
+
+        browser.selectedTab = browser.addTab("data:text/plain;base64," +
+          browserWindow.btoa(js_beautify(xhr.responseText, opts)));
+        promise.resolve();
+      }
+      else {
+        promise.resolve("Unable to load page to beautify: " + args.url + " " +
+                        xhr.status + " " + xhr.statusText);
+      }
+    };
+  }
+  xhr.send(null);
+  return promise;
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdPagemod.jsm
@@ -0,0 +1,264 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+
+/**
+ * 'pagemod' command
+ */
+gcli.addCommand({
+  name: "pagemod",
+  description: gcli.lookup("pagemodDesc"),
+});
+
+/**
+ * The 'pagemod replace' command. This command allows the user to search and
+ * replace within text nodes and attributes.
+ */
+gcli.addCommand({
+  name: "pagemod replace",
+  description: gcli.lookup("pagemodReplaceDesc"),
+  params: [
+    {
+      name: "search",
+      type: "string",
+      description: gcli.lookup("pagemodReplaceSearchDesc"),
+    },
+    {
+      name: "replace",
+      type: "string",
+      description: gcli.lookup("pagemodReplaceReplaceDesc"),
+    },
+    {
+      name: "ignoreCase",
+      type: "boolean",
+      description: gcli.lookup("pagemodReplaceIgnoreCaseDesc"),
+    },
+    {
+      name: "selector",
+      type: "string",
+      description: gcli.lookup("pagemodReplaceSelectorDesc"),
+      defaultValue: "*:not(script):not(style):not(embed):not(object):not(frame):not(iframe):not(frameset)",
+    },
+    {
+      name: "root",
+      type: "node",
+      description: gcli.lookup("pagemodReplaceRootDesc"),
+      defaultValue: null,
+    },
+    {
+      name: "attrOnly",
+      type: "boolean",
+      description: gcli.lookup("pagemodReplaceAttrOnlyDesc"),
+    },
+    {
+      name: "contentOnly",
+      type: "boolean",
+      description: gcli.lookup("pagemodReplaceContentOnlyDesc"),
+    },
+    {
+      name: "attributes",
+      type: "string",
+      description: gcli.lookup("pagemodReplaceAttributesDesc"),
+      defaultValue: null,
+    },
+  ],
+  exec: function(args, context) {
+    let document = context.environment.contentDocument;
+    let searchTextNodes = !args.attrOnly;
+    let searchAttributes = !args.contentOnly;
+    let regexOptions = args.ignoreCase ? 'ig' : 'g';
+    let search = new RegExp(escapeRegex(args.search), regexOptions);
+    let attributeRegex = null;
+    if (args.attributes) {
+      attributeRegex = new RegExp(args.attributes, regexOptions);
+    }
+
+    let root = args.root || document;
+    let elements = root.querySelectorAll(args.selector);
+    elements = Array.prototype.slice.call(elements);
+
+    let replacedTextNodes = 0;
+    let replacedAttributes = 0;
+
+    function replaceAttribute() {
+      replacedAttributes++;
+      return args.replace;
+    }
+    function replaceTextNode() {
+      replacedTextNodes++;
+      return args.replace;
+    }
+
+    for (let i = 0; i < elements.length; i++) {
+      let element = elements[i];
+      if (searchTextNodes) {
+        for (let y = 0; y < element.childNodes.length; y++) {
+          let node = element.childNodes[y];
+          if (node.nodeType == node.TEXT_NODE) {
+            node.textContent = node.textContent.replace(search, replaceTextNode);
+          }
+        }
+      }
+
+      if (searchAttributes) {
+        if (!element.attributes) {
+          continue;
+        }
+        for (let y = 0; y < element.attributes.length; y++) {
+          let attr = element.attributes[y];
+          if (!attributeRegex || attributeRegex.test(attr.name)) {
+            attr.value = attr.value.replace(search, replaceAttribute);
+          }
+        }
+      }
+    }
+
+    return gcli.lookupFormat("pagemodReplaceResult",
+                             [elements.length, replacedTextNodes,
+                              replacedAttributes]);
+  }
+});
+
+/**
+ * 'pagemod remove' command
+ */
+gcli.addCommand({
+  name: "pagemod remove",
+  description: gcli.lookup("pagemodRemoveDesc"),
+});
+
+
+/**
+ * The 'pagemod remove element' command.
+ */
+gcli.addCommand({
+  name: "pagemod remove element",
+  description: gcli.lookup("pagemodRemoveElementDesc"),
+  params: [
+    {
+      name: "search",
+      type: "string",
+      description: gcli.lookup("pagemodRemoveElementSearchDesc"),
+    },
+    {
+      name: "root",
+      type: "node",
+      description: gcli.lookup("pagemodRemoveElementRootDesc"),
+      defaultValue: null,
+    },
+    {
+      name: 'stripOnly',
+      type: 'boolean',
+      description: gcli.lookup("pagemodRemoveElementStripOnlyDesc"),
+    },
+    {
+      name: 'ifEmptyOnly',
+      type: 'boolean',
+      description: gcli.lookup("pagemodRemoveElementIfEmptyOnlyDesc"),
+    },
+  ],
+  exec: function(args, context) {
+    let document = context.environment.contentDocument;
+    let root = args.root || document;
+    let elements = Array.prototype.slice.call(root.querySelectorAll(args.search));
+
+    let removed = 0;
+    for (let i = 0; i < elements.length; i++) {
+      let element = elements[i];
+      let parentNode = element.parentNode;
+      if (!parentNode || !element.removeChild) {
+        continue;
+      }
+      if (args.stripOnly) {
+        while (element.hasChildNodes()) {
+          parentNode.insertBefore(element.childNodes[0], element);
+        }
+      }
+      if (!args.ifEmptyOnly || !element.hasChildNodes()) {
+        element.parentNode.removeChild(element);
+        removed++;
+      }
+    }
+
+    return gcli.lookupFormat("pagemodRemoveElementResultMatchedAndRemovedElements",
+                             [elements.length, removed]);
+  }
+});
+
+/**
+ * The 'pagemod remove attribute' command.
+ */
+gcli.addCommand({
+  name: "pagemod remove attribute",
+  description: gcli.lookup("pagemodRemoveAttributeDesc"),
+  params: [
+    {
+      name: "searchAttributes",
+      type: "string",
+      description: gcli.lookup("pagemodRemoveAttributeSearchAttributesDesc"),
+    },
+    {
+      name: "searchElements",
+      type: "string",
+      description: gcli.lookup("pagemodRemoveAttributeSearchElementsDesc"),
+    },
+    {
+      name: "root",
+      type: "node",
+      description: gcli.lookup("pagemodRemoveAttributeRootDesc"),
+      defaultValue: null,
+    },
+    {
+      name: "ignoreCase",
+      type: "boolean",
+      description: gcli.lookup("pagemodRemoveAttributeIgnoreCaseDesc"),
+    },
+  ],
+  exec: function(args, context) {
+    let document = context.environment.contentDocument;
+
+    let root = args.root || document;
+    let regexOptions = args.ignoreCase ? 'ig' : 'g';
+    let attributeRegex = new RegExp(args.searchAttributes, regexOptions);
+    let elements = root.querySelectorAll(args.searchElements);
+    elements = Array.prototype.slice.call(elements);
+
+    let removed = 0;
+    for (let i = 0; i < elements.length; i++) {
+      let element = elements[i];
+      if (!element.attributes) {
+        continue;
+      }
+
+      var attrs = Array.prototype.slice.call(element.attributes);
+      for (let y = 0; y < attrs.length; y++) {
+        let attr = attrs[y];
+        if (attributeRegex.test(attr.name)) {
+          element.removeAttribute(attr.name);
+          removed++;
+        }
+      }
+    }
+
+    return gcli.lookupFormat("pagemodRemoveAttributeResult",
+                             [elements.length, removed]);
+  }
+});
+
+/**
+ * Make a given string safe to use  in a regular expression.
+ *
+ * @param string aString
+ *        The string you want to use in a regex.
+ * @return string
+ *         The equivalent of |aString| but safe to use in a regex.
+ */
+function escapeRegex(aString) {
+  return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdRestart.jsm
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+/**
+ * Restart command
+ *
+ * @param boolean nocache
+ *        Disables loading content from cache upon restart.
+ *
+ * Examples :
+ * >> restart
+ * - restarts browser immediately
+ * >> restart --nocache
+ * - restarts immediately and starts Firefox without using cache
+ */
+gcli.addCommand({
+  name: "restart",
+  description: gcli.lookup("restartFirefoxDesc"),
+  params: [
+    {
+      name: "nocache",
+      type: "boolean",
+      defaultValue: false,
+      description: gcli.lookup("restartFirefoxNocacheDesc")
+    }
+  ],
+  returnType: "string",
+  exec: function Restart(args, context) {
+    let canceled = Cc["@mozilla.org/supports-PRBool;1"]
+                     .createInstance(Ci.nsISupportsPRBool);
+    Services.obs.notifyObservers(canceled, "quit-application-requested", "restart");
+    if (canceled.data) {
+      return gcli.lookup("restartFirefoxRequestCancelled");
+    }
+
+    // disable loading content from cache.
+    if (args.nocache) {
+      Services.appinfo.invalidateCachesOnRestart();
+    }
+
+    // restart
+    Cc['@mozilla.org/toolkit/app-startup;1']
+      .getService(Ci.nsIAppStartup)
+      .quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
+    return gcli.lookup("restartFirefoxRestarting");
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/CmdScreenshot.jsm
@@ -0,0 +1,135 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers",
+                                  "resource:///modules/devtools/LayoutHelpers.jsm");
+
+/**
+ * 'screenshot' command
+ */
+gcli.addCommand({
+  name: "screenshot",
+  description: gcli.lookup("screenshotDesc"),
+  manual: gcli.lookup("screenshotManual"),
+  returnType: "string",
+  params: [
+    {
+      name: "filename",
+      type: "string",
+      description: gcli.lookup("screenshotFilenameDesc"),
+      manual: gcli.lookup("screenshotFilenameManual")
+    },
+    {
+      name: "delay",
+      type: { name: "number", min: 0 },
+      defaultValue: 0,
+      description: gcli.lookup("screenshotDelayDesc"),
+      manual: gcli.lookup("screenshotDelayManual")
+    },
+    {
+      name: "fullpage",
+      type: "boolean",
+      defaultValue: false,
+      description: gcli.lookup("screenshotFullPageDesc"),
+      manual: gcli.lookup("screenshotFullPageManual")
+    },
+    {
+      name: "node",
+      type: "node",
+      defaultValue: null,
+      description: gcli.lookup("inspectNodeDesc"),
+      manual: gcli.lookup("inspectNodeManual")
+    }
+  ],
+  exec: function Command_screenshot(args, context) {
+    var document = context.environment.contentDocument;
+    if (args.delay > 0) {
+      var promise = context.createPromise();
+      document.defaultView.setTimeout(function Command_screenshotDelay() {
+        let reply = this.grabScreen(document, args.filename);
+        promise.resolve(reply);
+      }.bind(this), args.delay * 1000);
+      return promise;
+    }
+    else {
+      return this.grabScreen(document, args.filename, args.fullpage, args.node);
+    }
+  },
+  grabScreen:
+  function Command_screenshotGrabScreen(document, filename, fullpage, node) {
+    let window = document.defaultView;
+    let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+    let left = 0;
+    let top = 0;
+    let width;
+    let height;
+
+    if (!fullpage) {
+      if (!node) {
+        left = window.scrollX;
+        top = window.scrollY;
+        width = window.innerWidth;
+        height = window.innerHeight;
+      } else {
+        let rect = LayoutHelpers.getRect(node, window);
+        top = rect.top;
+        left = rect.left;
+        width = rect.width;
+        height = rect.height;
+      }
+    } else {
+      width = window.innerWidth + window.scrollMaxX;
+      height = window.innerHeight + window.scrollMaxY;
+    }
+    canvas.width = width;
+    canvas.height = height;
+
+    let ctx = canvas.getContext("2d");
+    ctx.drawWindow(window, left, top, width, height, "#fff");
+
+    let data = canvas.toDataURL("image/png", "");
+    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+
+    // Check there is a .png extension to filename
+    if (!filename.match(/.png$/i)) {
+      filename += ".png";
+    }
+
+    // If the filename is relative, tack it onto the download directory
+    if (!filename.match(/[\\\/]/)) {
+      let downloadMgr = Cc["@mozilla.org/download-manager;1"]
+        .getService(Ci.nsIDownloadManager);
+      let tempfile = downloadMgr.userDownloadsDirectory;
+      tempfile.append(filename);
+      filename = tempfile.path;
+    }
+
+    try {
+      file.initWithPath(filename);
+    } catch (ex) {
+      return "Error saving to " + filename;
+    }
+
+    let ioService = Cc["@mozilla.org/network/io-service;1"]
+      .getService(Ci.nsIIOService);
+
+    let Persist = Ci.nsIWebBrowserPersist;
+    let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+      .createInstance(Persist);
+    persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+                           Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+    let source = ioService.newURI(data, "UTF8", null);
+    persist.saveURI(source, null, null, null, null, file);
+
+    return "Saved to " + filename;
+  }
+});
rename from browser/devtools/commandline/GcliCommands.jsm
rename to browser/devtools/commandline/Commands.jsm
--- a/browser/devtools/commandline/GcliCommands.jsm
+++ b/browser/devtools/commandline/Commands.jsm
@@ -1,1549 +1,25 @@
 /* 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 EXPORTED_SYMBOLS = [ "GcliCommands" ];
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-const XMLHttpRequest =
-  Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
-
-Cu.import("resource:///modules/devtools/gcli.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
-                                  "resource:///modules/HUDService.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
-                                  "resource://gre/modules/NetUtil.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers",
-                                  "resource:///modules/devtools/LayoutHelpers.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "console",
-                                  "resource://gre/modules/devtools/Console.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
-                                  "resource://gre/modules/AddonManager.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "js_beautify",
-                                  "resource:///modules/devtools/Jsbeautify.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "Debugger", function() {
-  let JsDebugger = {};
-  Components.utils.import("resource://gre/modules/jsdebugger.jsm", JsDebugger);
-
-  let global = Components.utils.getGlobalForObject({});
-  JsDebugger.addDebuggerToGlobal(global);
-
-  return global.Debugger;
-});
-
-let prefSvc = "@mozilla.org/preferences-service;1";
-XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
-  let prefService = Cc[prefSvc].getService(Ci.nsIPrefService);
-  return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
-});
-
-Cu.import("resource:///modules/devtools/GcliTiltCommands.jsm", {});
-Cu.import("resource:///modules/devtools/GcliCookieCommands.jsm", {});
-
-/**
- * A place to store the names of the commands that we have added as a result of
- * calling refreshAutoCommands(). Used by refreshAutoCommands to remove the
- * added commands.
- */
-let commands = [];
-
-/**
- * Exported API
- */
-let GcliCommands = {
-  /**
-   * Called to look in a directory pointed at by the devtools.commands.dir pref
-   * for *.mozcmd files which are then loaded.
-   * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which
-   * we eval the script from the .mozcmd file. This should be a chrome window.
-   */
-  refreshAutoCommands: function GC_refreshAutoCommands(aSandboxPrincipal) {
-    // First get rid of the last set of commands
-    commands.forEach(function(name) {
-      gcli.removeCommand(name);
-    });
-
-    let dirName = prefBranch.getComplexValue("devtools.commands.dir",
-                                             Ci.nsISupportsString).data;
-    if (dirName == "") {
-      return;
-    }
-
-    let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
-    dir.initWithPath(dirName);
-    if (!dir.exists() || !dir.isDirectory()) {
-      throw new Error('\'' + dirName + '\' is not a directory.');
-    }
-
-    let en = dir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
-
-    while (true) {
-      let file = en.nextFile;
-      if (!file) {
-        break;
-      }
-      if (file.leafName.match(/.*\.mozcmd$/) && file.isFile() && file.isReadable()) {
-        loadCommandFile(file, aSandboxPrincipal);
-      }
-    }
-  },
-};
-
-/**
- * Load the commands from a single file
- * @param nsIFile aFile The file containing the commands that we should read
- * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which
- * we eval the script from the .mozcmd file. This should be a chrome window.
- */
-function loadCommandFile(aFile, aSandboxPrincipal) {
-  NetUtil.asyncFetch(aFile, function refresh_fetch(aStream, aStatus) {
-    if (!Components.isSuccessCode(aStatus)) {
-      console.error("NetUtil.asyncFetch(" + aFile.path + ",..) failed. Status=" + aStatus);
-      return;
-    }
-
-    let source = NetUtil.readInputStreamToString(aStream, aStream.available());
-    aStream.close();
-
-    let sandbox = new Cu.Sandbox(aSandboxPrincipal, {
-      sandboxPrototype: aSandboxPrincipal,
-      wantXrays: false,
-      sandboxName: aFile.path
-    });
-    let data = Cu.evalInSandbox(source, sandbox, "1.8", aFile.leafName, 1);
-
-    if (!Array.isArray(data)) {
-      console.error("Command file '" + aFile.leafName + "' does not have top level array.");
-      return;
-    }
-
-    data.forEach(function(commandSpec) {
-      gcli.addCommand(commandSpec);
-      commands.push(commandSpec.name);
-    });
-  }.bind(this));
-}
-
-/**
- * 'cmd' command
- */
-gcli.addCommand({
-  name: "cmd",
-  description: gcli.lookup("cmdDesc"),
-  hidden: true
-});
-
-/**
- * 'cmd refresh' command
- */
-gcli.addCommand({
-  name: "cmd refresh",
-  description: gcli.lookup("cmdRefreshDesc"),
-  hidden: true,
-  exec: function Command_cmdRefresh(args, context) {
-    GcliCommands.refreshAutoCommands(context.environment.chromeDocument.defaultView);
-  }
-});
-
-/**
- * 'echo' command
- */
-gcli.addCommand({
-  name: "echo",
-  description: gcli.lookup("echoDesc"),
-  params: [
-    {
-      name: "message",
-      type: "string",
-      description: gcli.lookup("echoMessageDesc")
-    }
-  ],
-  returnType: "string",
-  hidden: true,
-  exec: function Command_echo(args, context) {
-    return args.message;
-  }
-});
-
-
-/**
- * 'screenshot' command
- */
-gcli.addCommand({
-  name: "screenshot",
-  description: gcli.lookup("screenshotDesc"),
-  manual: gcli.lookup("screenshotManual"),
-  returnType: "string",
-  params: [
-    {
-      name: "filename",
-      type: "string",
-      description: gcli.lookup("screenshotFilenameDesc"),
-      manual: gcli.lookup("screenshotFilenameManual")
-    },
-    {
-      name: "delay",
-      type: { name: "number", min: 0 },
-      defaultValue: 0,
-      description: gcli.lookup("screenshotDelayDesc"),
-      manual: gcli.lookup("screenshotDelayManual")
-    },
-    {
-      name: "fullpage",
-      type: "boolean",
-      defaultValue: false,
-      description: gcli.lookup("screenshotFullPageDesc"),
-      manual: gcli.lookup("screenshotFullPageManual")
-    },
-    {
-      name: "node",
-      type: "node",
-      defaultValue: null,
-      description: gcli.lookup("inspectNodeDesc"),
-      manual: gcli.lookup("inspectNodeManual")
-    }
-  ],
-  exec: function Command_screenshot(args, context) {
-    var document = context.environment.contentDocument;
-    if (args.delay > 0) {
-      var promise = context.createPromise();
-      document.defaultView.setTimeout(function Command_screenshotDelay() {
-        let reply = this.grabScreen(document, args.filename);
-        promise.resolve(reply);
-      }.bind(this), args.delay * 1000);
-      return promise;
-    }
-    else {
-      return this.grabScreen(document, args.filename, args.fullpage, args.node);
-    }
-  },
-  grabScreen:
-  function Command_screenshotGrabScreen(document, filename, fullpage, node) {
-    let window = document.defaultView;
-    let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-    let left = 0;
-    let top = 0;
-    let width;
-    let height;
-
-    if (!fullpage) {
-      if (!node) {
-        left = window.scrollX;
-        top = window.scrollY;
-        width = window.innerWidth;
-        height = window.innerHeight;
-      } else {
-        let rect = LayoutHelpers.getRect(node, window);
-        top = rect.top;
-        left = rect.left;
-        width = rect.width;
-        height = rect.height;
-      }
-    } else {
-      width = window.innerWidth + window.scrollMaxX;
-      height = window.innerHeight + window.scrollMaxY;
-    }
-    canvas.width = width;
-    canvas.height = height;
-
-    let ctx = canvas.getContext("2d");
-    ctx.drawWindow(window, left, top, width, height, "#fff");
-
-    let data = canvas.toDataURL("image/png", "");
-    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
-
-    // Check there is a .png extension to filename
-    if (!filename.match(/.png$/i)) {
-      filename += ".png";
-    }
-
-    // If the filename is relative, tack it onto the download directory
-    if (!filename.match(/[\\\/]/)) {
-      let downloadMgr = Cc["@mozilla.org/download-manager;1"]
-        .getService(Ci.nsIDownloadManager);
-      let tempfile = downloadMgr.userDownloadsDirectory;
-      tempfile.append(filename);
-      filename = tempfile.path;
-    }
-
-    try {
-      file.initWithPath(filename);
-    } catch (ex) {
-      return "Error saving to " + filename;
-    }
-
-    let ioService = Cc["@mozilla.org/network/io-service;1"]
-      .getService(Ci.nsIIOService);
-
-    let Persist = Ci.nsIWebBrowserPersist;
-    let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
-      .createInstance(Persist);
-    persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
-                           Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
-
-    let source = ioService.newURI(data, "UTF8", null);
-    persist.saveURI(source, null, null, null, null, file);
-
-    return "Saved to " + filename;
-  }
-});
-
-
-let callLogDebuggers = [];
-
-/**
- * 'calllog' command
- */
-gcli.addCommand({
-  name: "calllog",
-  description: gcli.lookup("calllogDesc")
-})
-
-/**
- * 'calllog start' command
- */
-gcli.addCommand({
-  name: "calllog start",
-  description: gcli.lookup("calllogStartDesc"),
-
-  exec: function(args, context) {
-    let contentWindow = context.environment.contentDocument.defaultView;
-
-    let dbg = new Debugger(contentWindow);
-    dbg.onEnterFrame = function(frame) {
-      // BUG 773652 -  Make the output from the GCLI calllog command nicer
-      contentWindow.console.log("Method call: " + this.callDescription(frame));
-    }.bind(this);
-
-    callLogDebuggers.push(dbg);
-
-    let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab;
-    HUDService.activateHUDForContext(tab);
-
-    return gcli.lookup("calllogStartReply");
-  },
-
-  callDescription: function(frame) {
-    let name = "<anonymous>";
-    if (frame.callee.name) {
-      name = frame.callee.name;
-    }
-    else {
-      let desc = frame.callee.getOwnPropertyDescriptor("displayName");
-      if (desc && desc.value && typeof desc.value == "string") {
-        name = desc.value;
-      }
-    }
-
-    let args = frame.arguments.map(this.valueToString).join(", ");
-    return name + "(" + args + ")";
-  },
-
-  valueToString: function(value) {
-    if (typeof value !== "object" || value === null) {
-      return uneval(value);
-    }
-    return "[object " + value.class + "]";
-  }
-});
+let EXPORTED_SYMBOLS = [ ];
 
-/**
- * 'calllog stop' command
- */
-gcli.addCommand({
-  name: "calllog stop",
-  description: gcli.lookup("calllogStopDesc"),
-
-  exec: function(args, context) {
-    let numDebuggers = callLogDebuggers.length;
-    if (numDebuggers == 0) {
-      return gcli.lookup("calllogStopNoLogging");
-    }
-
-    for (let dbg of callLogDebuggers) {
-      dbg.onEnterFrame = undefined;
-    }
-    callLogDebuggers = [];
-
-    return gcli.lookupFormat("calllogStopReply", [ numDebuggers ]);
-  }
-});
-
-
-/**
- * 'console' command
- */
-gcli.addCommand({
-  name: "console",
-  description: gcli.lookup("consoleDesc"),
-  manual: gcli.lookup("consoleManual")
-});
-
-/**
- * 'console clear' command
- */
-gcli.addCommand({
-  name: "console clear",
-  description: gcli.lookup("consoleclearDesc"),
-  exec: function Command_consoleClear(args, context) {
-    let window = context.environment.contentDocument.defaultView;
-    let hud = HUDService.getHudByWindow(window);
-    // hud will be null if the web console has not been opened for this window
-    if (hud) {
-      hud.jsterm.clearOutput();
-    }
-  }
-});
-
-/**
- * 'console close' command
- */
-gcli.addCommand({
-  name: "console close",
-  description: gcli.lookup("consolecloseDesc"),
-  exec: function Command_consoleClose(args, context) {
-    let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab
-    HUDService.deactivateHUDForContext(tab);
-  }
-});
-
-/**
- * 'console open' command
- */
-gcli.addCommand({
-  name: "console open",
-  description: gcli.lookup("consoleopenDesc"),
-  exec: function Command_consoleOpen(args, context) {
-    let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab
-    HUDService.activateHUDForContext(tab);
-  }
-});
-
-/**
- * Restart command
- *
- * @param boolean nocache
- *        Disables loading content from cache upon restart.
- *
- * Examples :
- * >> restart
- * - restarts browser immediately
- * >> restart --nocache
- * - restarts immediately and starts Firefox without using cache
- */
-gcli.addCommand({
-  name: "restart",
-  description: gcli.lookup("restartFirefoxDesc"),
-  params: [
-    {
-      name: "nocache",
-      type: "boolean",
-      defaultValue: false,
-      description: gcli.lookup("restartFirefoxNocacheDesc")
-    }
-  ],
-  returnType: "string",
-  exec: function Restart(args, context) {
-    let canceled = Cc["@mozilla.org/supports-PRBool;1"]
-                     .createInstance(Ci.nsISupportsPRBool);
-    Services.obs.notifyObservers(canceled, "quit-application-requested", "restart");
-    if (canceled.data) {
-      return gcli.lookup("restartFirefoxRequestCancelled");
-    }
-
-    // disable loading content from cache.
-    if (args.nocache) {
-      Services.appinfo.invalidateCachesOnRestart();
-    }
-
-    // restart
-    Cc['@mozilla.org/toolkit/app-startup;1']
-      .getService(Ci.nsIAppStartup)
-      .quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
-    return gcli.lookup("restartFirefoxRestarting");
-  }
-});
-
-/**
- * 'inspect' command
- */
-gcli.addCommand({
-  name: "inspect",
-  description: gcli.lookup("inspectDesc"),
-  manual: gcli.lookup("inspectManual"),
-  params: [
-    {
-      name: "node",
-      type: "node",
-      description: gcli.lookup("inspectNodeDesc"),
-      manual: gcli.lookup("inspectNodeManual")
-    }
-  ],
-  exec: function Command_inspect(args, context) {
-    let document = context.environment.chromeDocument;
-    document.defaultView.InspectorUI.openInspectorUI(args.node);
-  }
-});
-
-/**
- * 'edit' command
- */
-gcli.addCommand({
-  name: "edit",
-  description: gcli.lookup("editDesc"),
-  manual: gcli.lookup("editManual2"),
-  params: [
-     {
-       name: 'resource',
-       type: {
-         name: 'resource',
-         include: 'text/css'
-       },
-       description: gcli.lookup("editResourceDesc")
-     },
-     {
-       name: "line",
-       defaultValue: 1,
-       type: {
-         name: "number",
-         min: 1,
-         step: 10
-       },
-       description: gcli.lookup("editLineToJumpToDesc")
-     }
-   ],
-   exec: function(args, context) {
-     let win = HUDService.currentContext();
-     win.StyleEditor.openChrome(args.resource.element, args.line);
-   }
-});
-
-/**
- * 'break' command
- */
-gcli.addCommand({
-  name: "break",
-  description: gcli.lookup("breakDesc"),
-  manual: gcli.lookup("breakManual")
-});
-
-
-/**
- * 'break list' command
- */
-gcli.addCommand({
-  name: "break list",
-  description: gcli.lookup("breaklistDesc"),
-  returnType: "html",
-  exec: function(args, context) {
-    let win = HUDService.currentContext();
-    let dbg = win.DebuggerUI.getDebugger();
-    if (!dbg) {
-      return gcli.lookup("breakaddDebuggerStopped");
-    }
-    let breakpoints = dbg.breakpoints;
-
-    if (Object.keys(breakpoints).length === 0) {
-      return gcli.lookup("breaklistNone");
-    }
-
-    let reply = gcli.lookup("breaklistIntro");
-    reply += "<ol>";
-    for each (let breakpoint in breakpoints) {
-      let text = gcli.lookupFormat("breaklistLineEntry",
-                                   [breakpoint.location.url,
-                                    breakpoint.location.line]);
-      reply += "<li>" + text + "</li>";
-    };
-    reply += "</ol>";
-    return reply;
-  }
-});
-
-
-/**
- * 'break add' command
- */
-gcli.addCommand({
-  name: "break add",
-  description: gcli.lookup("breakaddDesc"),
-  manual: gcli.lookup("breakaddManual")
-});
-
-/**
- * 'break add line' command
- */
-gcli.addCommand({
-  name: "break add line",
-  description: gcli.lookup("breakaddlineDesc"),
-  params: [
-    {
-      name: "file",
-      type: {
-        name: "selection",
-        data: function() {
-          let win = HUDService.currentContext();
-          let dbg = win.DebuggerUI.getDebugger();
-          let files = [];
-          if (dbg) {
-            let scriptsView = dbg.contentWindow.DebuggerView.Scripts;
-            for each (let script in scriptsView.scriptLocations) {
-              files.push(script);
-            }
-          }
-          return files;
-        }
-      },
-      description: gcli.lookup("breakaddlineFileDesc")
-    },
-    {
-      name: "line",
-      type: { name: "number", min: 1, step: 10 },
-      description: gcli.lookup("breakaddlineLineDesc")
-    }
-  ],
-  returnType: "html",
-  exec: function(args, context) {
-    args.type = "line";
-    let win = HUDService.currentContext();
-    let dbg = win.DebuggerUI.getDebugger();
-    if (!dbg) {
-      return gcli.lookup("breakaddDebuggerStopped");
-    }
-    var promise = context.createPromise();
-    let position = { url: args.file, line: args.line };
-    dbg.addBreakpoint(position, function(aBreakpoint, aError) {
-      if (aError) {
-        promise.resolve(gcli.lookupFormat("breakaddFailed", [aError]));
-        return;
-      }
-      promise.resolve(gcli.lookup("breakaddAdded"));
-    });
-    return promise;
-  }
-});
-
-
-/**
- * 'break del' command
- */
-gcli.addCommand({
-  name: "break del",
-  description: gcli.lookup("breakdelDesc"),
-  params: [
-    {
-      name: "breakid",
-      type: {
-        name: "number",
-        min: 0,
-        max: function() {
-          let win = HUDService.currentContext();
-          let dbg = win.DebuggerUI.getDebugger();
-          if (!dbg) {
-            return gcli.lookup("breakaddDebuggerStopped");
-          }
-          return Object.keys(dbg.breakpoints).length - 1;
-        },
-      },
-      description: gcli.lookup("breakdelBreakidDesc")
-    }
-  ],
-  returnType: "html",
-  exec: function(args, context) {
-    let win = HUDService.currentContext();
-    let dbg = win.DebuggerUI.getDebugger();
-    if (!dbg) {
-      return gcli.lookup("breakaddDebuggerStopped");
-    }
-
-    let breakpoints = dbg.breakpoints;
-    let id = Object.keys(dbg.breakpoints)[args.breakid];
-    if (!id || !(id in breakpoints)) {
-      return gcli.lookup("breakNotFound");
-    }
-
-    let promise = context.createPromise();
-    try {
-      dbg.removeBreakpoint(breakpoints[id], function() {
-        promise.resolve(gcli.lookup("breakdelRemoved"));
-      });
-    } catch (ex) {
-      // If the debugger has been closed already, don't scare the user.
-      promise.resolve(gcli.lookup("breakdelRemoved"));
-    }
-    return promise;
-  }
-});
-
-/**
- * 'export' command
- */
-gcli.addCommand({
-  name: "export",
-  description: gcli.lookup("exportDesc"),
-});
-
-/**
- * The 'export html' command. This command allows the user to export the page to
- * HTML after they do DOM changes.
- */
-gcli.addCommand({
-  name: "export html",
-  description: gcli.lookup("exportHtmlDesc"),
-  exec: function(args, context) {
-    let document = context.environment.contentDocument;
-    let window = document.defaultView;
-    let page = document.documentElement.outerHTML;
-    window.open('data:text/plain;charset=utf8,' + encodeURIComponent(page));
-  }
-});
-
-/**
- * 'pagemod' command
- */
-gcli.addCommand({
-  name: "pagemod",
-  description: gcli.lookup("pagemodDesc"),
-});
+const Cu = Components.utils;
 
-/**
- * The 'pagemod replace' command. This command allows the user to search and
- * replace within text nodes and attributes.
- */
-gcli.addCommand({
-  name: "pagemod replace",
-  description: gcli.lookup("pagemodReplaceDesc"),
-  params: [
-    {
-      name: "search",
-      type: "string",
-      description: gcli.lookup("pagemodReplaceSearchDesc"),
-    },
-    {
-      name: "replace",
-      type: "string",
-      description: gcli.lookup("pagemodReplaceReplaceDesc"),
-    },
-    {
-      name: "ignoreCase",
-      type: "boolean",
-      description: gcli.lookup("pagemodReplaceIgnoreCaseDesc"),
-    },
-    {
-      name: "selector",
-      type: "string",
-      description: gcli.lookup("pagemodReplaceSelectorDesc"),
-      defaultValue: "*:not(script):not(style):not(embed):not(object):not(frame):not(iframe):not(frameset)",
-    },
-    {
-      name: "root",
-      type: "node",
-      description: gcli.lookup("pagemodReplaceRootDesc"),
-      defaultValue: null,
-    },
-    {
-      name: "attrOnly",
-      type: "boolean",
-      description: gcli.lookup("pagemodReplaceAttrOnlyDesc"),
-    },
-    {
-      name: "contentOnly",
-      type: "boolean",
-      description: gcli.lookup("pagemodReplaceContentOnlyDesc"),
-    },
-    {
-      name: "attributes",
-      type: "string",
-      description: gcli.lookup("pagemodReplaceAttributesDesc"),
-      defaultValue: null,
-    },
-  ],
-  exec: function(args, context) {
-    let document = context.environment.contentDocument;
-    let searchTextNodes = !args.attrOnly;
-    let searchAttributes = !args.contentOnly;
-    let regexOptions = args.ignoreCase ? 'ig' : 'g';
-    let search = new RegExp(escapeRegex(args.search), regexOptions);
-    let attributeRegex = null;
-    if (args.attributes) {
-      attributeRegex = new RegExp(args.attributes, regexOptions);
-    }
-
-    let root = args.root || document;
-    let elements = root.querySelectorAll(args.selector);
-    elements = Array.prototype.slice.call(elements);
-
-    let replacedTextNodes = 0;
-    let replacedAttributes = 0;
-
-    function replaceAttribute() {
-      replacedAttributes++;
-      return args.replace;
-    }
-    function replaceTextNode() {
-      replacedTextNodes++;
-      return args.replace;
-    }
-
-    for (let i = 0; i < elements.length; i++) {
-      let element = elements[i];
-      if (searchTextNodes) {
-        for (let y = 0; y < element.childNodes.length; y++) {
-          let node = element.childNodes[y];
-          if (node.nodeType == node.TEXT_NODE) {
-            node.textContent = node.textContent.replace(search, replaceTextNode);
-          }
-        }
-      }
-
-      if (searchAttributes) {
-        if (!element.attributes) {
-          continue;
-        }
-        for (let y = 0; y < element.attributes.length; y++) {
-          let attr = element.attributes[y];
-          if (!attributeRegex || attributeRegex.test(attr.name)) {
-            attr.value = attr.value.replace(search, replaceAttribute);
-          }
-        }
-      }
-    }
-
-    return gcli.lookupFormat("pagemodReplaceResult",
-                             [elements.length, replacedTextNodes,
-                              replacedAttributes]);
-  }
-});
-
-/**
- * 'pagemod remove' command
- */
-gcli.addCommand({
-  name: "pagemod remove",
-  description: gcli.lookup("pagemodRemoveDesc"),
-});
-
-
-/**
- * The 'pagemod remove element' command.
- */
-gcli.addCommand({
-  name: "pagemod remove element",
-  description: gcli.lookup("pagemodRemoveElementDesc"),
-  params: [
-    {
-      name: "search",
-      type: "string",
-      description: gcli.lookup("pagemodRemoveElementSearchDesc"),
-    },
-    {
-      name: "root",
-      type: "node",
-      description: gcli.lookup("pagemodRemoveElementRootDesc"),
-      defaultValue: null,
-    },
-    {
-      name: 'stripOnly',
-      type: 'boolean',
-      description: gcli.lookup("pagemodRemoveElementStripOnlyDesc"),
-    },
-    {
-      name: 'ifEmptyOnly',
-      type: 'boolean',
-      description: gcli.lookup("pagemodRemoveElementIfEmptyOnlyDesc"),
-    },
-  ],
-  exec: function(args, context) {
-    let document = context.environment.contentDocument;
-    let root = args.root || document;
-    let elements = Array.prototype.slice.call(root.querySelectorAll(args.search));
-
-    let removed = 0;
-    for (let i = 0; i < elements.length; i++) {
-      let element = elements[i];
-      let parentNode = element.parentNode;
-      if (!parentNode || !element.removeChild) {
-        continue;
-      }
-      if (args.stripOnly) {
-        while (element.hasChildNodes()) {
-          parentNode.insertBefore(element.childNodes[0], element);
-        }
-      }
-      if (!args.ifEmptyOnly || !element.hasChildNodes()) {
-        element.parentNode.removeChild(element);
-        removed++;
-      }
-    }
-
-    return gcli.lookupFormat("pagemodRemoveElementResultMatchedAndRemovedElements",
-                             [elements.length, removed]);
-  }
-});
-
-/**
- * The 'pagemod remove attribute' command.
- */
-gcli.addCommand({
-  name: "pagemod remove attribute",
-  description: gcli.lookup("pagemodRemoveAttributeDesc"),
-  params: [
-    {
-      name: "searchAttributes",
-      type: "string",
-      description: gcli.lookup("pagemodRemoveAttributeSearchAttributesDesc"),
-    },
-    {
-      name: "searchElements",
-      type: "string",
-      description: gcli.lookup("pagemodRemoveAttributeSearchElementsDesc"),
-    },
-    {
-      name: "root",
-      type: "node",
-      description: gcli.lookup("pagemodRemoveAttributeRootDesc"),
-      defaultValue: null,
-    },
-    {
-      name: "ignoreCase",
-      type: "boolean",
-      description: gcli.lookup("pagemodRemoveAttributeIgnoreCaseDesc"),
-    },
-  ],
-  exec: function(args, context) {
-    let document = context.environment.contentDocument;
-
-    let root = args.root || document;
-    let regexOptions = args.ignoreCase ? 'ig' : 'g';
-    let attributeRegex = new RegExp(args.searchAttributes, regexOptions);
-    let elements = root.querySelectorAll(args.searchElements);
-    elements = Array.prototype.slice.call(elements);
-
-    let removed = 0;
-    for (let i = 0; i < elements.length; i++) {
-      let element = elements[i];
-      if (!element.attributes) {
-        continue;
-      }
-
-      var attrs = Array.prototype.slice.call(element.attributes);
-      for (let y = 0; y < attrs.length; y++) {
-        let attr = attrs[y];
-        if (attributeRegex.test(attr.name)) {
-          element.removeAttribute(attr.name);
-          removed++;
-        }
-      }
-    }
-
-    return gcli.lookupFormat("pagemodRemoveAttributeResult",
-                             [elements.length, removed]);
-  }
-});
-
-
-/**
- * Make a given string safe to use  in a regular expression.
- *
- * @param string aString
- *        The string you want to use in a regex.
- * @return string
- *         The equivalent of |aString| but safe to use in a regex.
- */
-function escapeRegex(aString) {
-  return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
-}
-
-/**
- * 'addon' command.
- */
-gcli.addCommand({
-  name: "addon",
-  description: gcli.lookup("addonDesc")
-});
-
-/**
- * 'addon list' command.
- */
-gcli.addCommand({
-  name: "addon list",
-  description: gcli.lookup("addonListDesc"),
-  params: [{
-    name: 'type',
-    type: {
-      name: 'selection',
-      data: ["dictionary", "extension", "locale", "plugin", "theme", "all"]
-    },
-    defaultValue: 'all',
-    description: gcli.lookup("addonListTypeDesc"),
-  }],
-  exec: function(aArgs, context) {
-    function representEnabledAddon(aAddon) {
-      return "<li><![CDATA[" + aAddon.name + "\u2002" + aAddon.version +
-      getAddonStatus(aAddon) + "]]></li>";
-    }
-
-    function representDisabledAddon(aAddon) {
-      return "<li class=\"gcli-addon-disabled\">" +
-        "<![CDATA[" + aAddon.name + "\u2002" + aAddon.version + aAddon.version +
-        "]]></li>";
-    }
-
-    function getAddonStatus(aAddon) {
-      let operations = [];
-
-      if (aAddon.pendingOperations & AddonManager.PENDING_ENABLE) {
-        operations.push("PENDING_ENABLE");
-      }
-
-      if (aAddon.pendingOperations & AddonManager.PENDING_DISABLE) {
-        operations.push("PENDING_DISABLE");
-      }
-
-      if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
-        operations.push("PENDING_UNINSTALL");
-      }
-
-      if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL) {
-        operations.push("PENDING_INSTALL");
-      }
-
-      if (aAddon.pendingOperations & AddonManager.PENDING_UPGRADE) {
-        operations.push("PENDING_UPGRADE");
-      }
-
-      if (operations.length) {
-        return " (" + operations.join(", ") + ")";
-      }
-      return "";
-    }
-
-    /**
-     * Compares two addons by their name. Used in sorting.
-     */
-    function compareAddonNames(aNameA, aNameB) {
-      return String.localeCompare(aNameA.name, aNameB.name);
-    }
-
-    /**
-     * Resolves the promise which is the scope (this) of this function, filling
-     * it with an HTML representation of the passed add-ons.
-     */
-    function list(aType, aAddons) {
-      if (!aAddons.length) {
-        this.resolve(gcli.lookup("addonNoneOfType"));
-      }
-
-      // Separate the enabled add-ons from the disabled ones.
-      let enabledAddons = [];
-      let disabledAddons = [];
-
-      aAddons.forEach(function(aAddon) {
-        if (aAddon.isActive) {
-          enabledAddons.push(aAddon);
-        } else {
-          disabledAddons.push(aAddon);
-        }
-      });
-
-      let header;
-      switch(aType) {
-        case "dictionary":
-          header = gcli.lookup("addonListDictionaryHeading");
-          break;
-        case "extension":
-          header = gcli.lookup("addonListExtensionHeading");
-          break;
-        case "locale":
-          header = gcli.lookup("addonListLocaleHeading");
-          break;
-        case "plugin":
-          header = gcli.lookup("addonListPluginHeading");
-          break;
-        case "theme":
-          header = gcli.lookup("addonListThemeHeading");
-        case "all":
-          header = gcli.lookup("addonListAllHeading");
-          break;
-        default:
-          header = gcli.lookup("addonListUnknownHeading");
-      }
-
-      // Map and sort the add-ons, and create an HTML list.
-      this.resolve(header +
-        "<ol>" +
-        enabledAddons.sort(compareAddonNames).map(representEnabledAddon).join("") +
-        disabledAddons.sort(compareAddonNames).map(representDisabledAddon).join("") +
-        "</ol>");
-    }
-
-    // Create the promise that will be resolved when the add-on listing has
-    // been finished.
-    let promise = context.createPromise();
-    let types = aArgs.type == "all" ? null : [aArgs.type];
-    AddonManager.getAddonsByTypes(types, list.bind(promise, aArgs.type));
-    return promise;
-  }
-});
-
-
-/**
- * 'dbg' command
- */
-gcli.addCommand({
-  name: "dbg",
-  description: gcli.lookup("dbgDesc"),
-  manual: gcli.lookup("dbgManual")
-});
-
-
-/**
- * 'dbg interrupt' command
- */
-gcli.addCommand({
-  name: "dbg interrupt",
-  description: gcli.lookup("dbgInterrupt"),
-  params: [],
-  exec: function(args, context) {
-    let win = context.environment.chromeDocument.defaultView;
-    let dbg = win.DebuggerUI.getDebugger();
-
-    if (dbg) {
-      let controller = dbg.contentWindow.DebuggerController;
-      let thread = controller.activeThread;
-      if (!thread.paused) {
-        thread.interrupt();
-      }
-    }
-  }
-});
-
-/**
- * 'dbg continue' command
- */
-gcli.addCommand({
-  name: "dbg continue",
-  description: gcli.lookup("dbgContinue"),
-  params: [],
-  exec: function(args, context) {
-    let win = context.environment.chromeDocument.defaultView;
-    let dbg = win.DebuggerUI.getDebugger();
-
-    if (dbg) {
-      let controller = dbg.contentWindow.DebuggerController;
-      let thread = controller.activeThread;
-      if (thread.paused) {
-        thread.resume();
-      }
-    }
-  }
-});
-
-
-/**
- * 'dbg step' command
- */
-gcli.addCommand({
-  name: "dbg step",
-  description: gcli.lookup("dbgStepDesc"),
-  manual: gcli.lookup("dbgStepManual")
-});
-
-
-/**
- * 'dbg step over' command
- */
-gcli.addCommand({
-  name: "dbg step over",
-  description: gcli.lookup("dbgStepOverDesc"),
-  params: [],
-  exec: function(args, context) {
-    let win = context.environment.chromeDocument.defaultView;
-    let dbg = win.DebuggerUI.getDebugger();
-
-    if (dbg) {
-      let controller = dbg.contentWindow.DebuggerController;
-      let thread = controller.activeThread;
-      if (thread.paused) {
-        thread.stepOver();
-      }
-    }
-  }
-});
-
-/**
- * 'dbg step in' command
- */
-gcli.addCommand({
-  name: 'dbg step in',
-  description: gcli.lookup("dbgStepInDesc"),
-  params: [],
-  exec: function(args, context) {
-    let win = context.environment.chromeDocument.defaultView;
-    let dbg = win.DebuggerUI.getDebugger();
-
-    if (dbg) {
-      let controller = dbg.contentWindow.DebuggerController;
-      let thread = controller.activeThread;
-      if (thread.paused) {
-        thread.stepIn();
-      }
-    }
-  }
-});
-
-/**
- * 'dbg step over' command
- */
-gcli.addCommand({
-  name: 'dbg step out',
-  description: gcli.lookup("dbgStepOutDesc"),
-  params: [],
-  exec: function(args, context) {
-    let win = context.environment.chromeDocument.defaultView;
-    let dbg = win.DebuggerUI.getDebugger();
-
-    if (dbg) {
-      let controller = dbg.contentWindow.DebuggerController;
-      let thread = controller.activeThread;
-      if (thread.paused) {
-        thread.stepOut();
-      }
-    }
-  }
-});
-
-// We need a list of addon names for the enable and disable commands. Because
-// getting the name list is async we do not add the commands until we have the
-// list.
-AddonManager.getAllAddons(function addonAsync(aAddons) {
-  // We listen for installs to keep our addon list up to date. There is no need
-  // to listen for uninstalls because uninstalled addons are simply disabled
-  // until restart (to enable undo functionality).
-  AddonManager.addAddonListener({
-    onInstalled: function(aAddon) {
-      addonNameCache.push({
-        name: representAddon(aAddon).replace(/\s/g, "_"),
-        value: aAddon.name
-      });
-    },
-    onUninstalled: function(aAddon) {
-      let name = representAddon(aAddon).replace(/\s/g, "_");
-
-      for (let i = 0; i < addonNameCache.length; i++) {
-        if(addonNameCache[i].name == name) {
-          addonNameCache.splice(i, 1);
-          break;
-        }
-      }
-    },
-  });
-
-  /**
-   * Returns a string that represents the passed add-on.
-   */
-  function representAddon(aAddon) {
-    let name = aAddon.name + " " + aAddon.version;
-    return name.trim();
-  }
-
-  let addonNameCache = [];
-
-  // The name parameter, used in "addon enable" and "addon disable."
-  let nameParameter = {
-    name: "name",
-    type: {
-      name: "selection",
-      lookup: addonNameCache
-    },
-    description: gcli.lookup("addonNameDesc")
-  };
-
-  for (let addon of aAddons) {
-    addonNameCache.push({
-      name: representAddon(addon).replace(/\s/g, "_"),
-      value: addon.name
-    });
-  }
-
-  /**
-   * 'addon enable' command.
-   */
-  gcli.addCommand({
-    name: "addon enable",
-    description: gcli.lookup("addonEnableDesc"),
-    params: [nameParameter],
-    exec: function(aArgs, context) {
-      /**
-       * Enables the addon in the passed list which has a name that matches
-       * according to the passed name comparer, and resolves the promise which
-       * is the scope (this) of this function to display the result of this
-       * enable attempt.
-       */
-      function enable(aName, addons) {
-        // Find the add-on.
-        let addon = null;
-        addons.some(function(candidate) {
-          if (candidate.name == aName) {
-            addon = candidate;
-            return true;
-          } else {
-            return false;
-          }
-        });
-
-        let name = representAddon(addon);
-
-        if (!addon.userDisabled) {
-          this.resolve("<![CDATA[" +
-            gcli.lookupFormat("addonAlreadyEnabled", [name]) + "]]>");
-        } else {
-          addon.userDisabled = false;
-          // nl-nl: {$1} is ingeschakeld.
-          this.resolve("<![CDATA[" +
-            gcli.lookupFormat("addonEnabled", [name]) + "]]>");
-        }
-      }
-
-      let promise = context.createPromise();
-      // List the installed add-ons, enable one when done listing.
-      AddonManager.getAllAddons(enable.bind(promise, aArgs.name));
-      return promise;
-    }
-  });
-
-  /**
-   * 'addon disable' command.
-   */
-  gcli.addCommand({
-    name: "addon disable",
-    description: gcli.lookup("addonDisableDesc"),
-    params: [nameParameter],
-    exec: function(aArgs, context) {
-      /**
-       * Like enable, but ... you know ... the exact opposite.
-       */
-      function disable(aName, addons) {
-        // Find the add-on.
-        let addon = null;
-        addons.some(function(candidate) {
-          if (candidate.name == aName) {
-            addon = candidate;
-            return true;
-          } else {
-            return false;
-          }
-        });
-
-        let name = representAddon(addon);
-
-        if (addon.userDisabled) {
-          this.resolve("<![CDATA[" +
-            gcli.lookupFormat("addonAlreadyDisabled", [name]) + "]]>");
-        } else {
-          addon.userDisabled = true;
-          // nl-nl: {$1} is uitgeschakeld.
-          this.resolve("<![CDATA[" +
-            gcli.lookupFormat("addonDisabled", [name]) + "]]>");
-        }
-      }
-
-      let promise = context.createPromise();
-      // List the installed add-ons, disable one when done listing.
-      AddonManager.getAllAddons(disable.bind(promise, aArgs.name));
-      return promise;
-    }
-  });
-  Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null);
-});
-
-/* Responsive Mode commands */
-(function gcli_cmd_resize_container() {
-  function gcli_cmd_resize(args, context) {
-    let browserDoc = context.environment.chromeDocument;
-    let browserWindow = browserDoc.defaultView;
-    let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager;
-    mgr.handleGcliCommand(browserWindow,
-                          browserWindow.gBrowser.selectedTab,
-                          this.name,
-                          args);
-  }
-
-  gcli.addCommand({
-    name: 'resize',
-    description: gcli.lookup('resizeModeDesc')
-  });
-
-  gcli.addCommand({
-    name: 'resize on',
-    description: gcli.lookup('resizeModeOnDesc'),
-    manual: gcli.lookup('resizeModeManual'),
-    exec: gcli_cmd_resize
-  });
-
-  gcli.addCommand({
-    name: 'resize off',
-    description: gcli.lookup('resizeModeOffDesc'),
-    manual: gcli.lookup('resizeModeManual'),
-    exec: gcli_cmd_resize
-  });
-
-  gcli.addCommand({
-    name: 'resize toggle',
-    description: gcli.lookup('resizeModeToggleDesc'),
-    manual: gcli.lookup('resizeModeManual'),
-    exec: gcli_cmd_resize
-  });
-
-  gcli.addCommand({
-    name: 'resize to',
-    description: gcli.lookup('resizeModeToDesc'),
-    params: [
-      {
-        name: 'width',
-        type: 'number',
-        description: gcli.lookup("resizePageArgWidthDesc"),
-      },
-      {
-        name: 'height',
-        type: 'number',
-        description: gcli.lookup("resizePageArgHeightDesc"),
-      },
-    ],
-    exec: gcli_cmd_resize
-  });
-})();
-
-/**
- * jsb command.
- */
-gcli.addCommand({
-  name: 'jsb',
-  description: gcli.lookup('jsbDesc'),
-  returnValue:'string',
-  hidden: true,
-  params: [
-    {
-      name: 'url',
-      type: 'string',
-      description: gcli.lookup('jsbUrlDesc'),
-      manual: 'The URL of the JS to prettify'
-    },
-    {
-      name: 'indentSize',
-      type: 'number',
-      description: gcli.lookup('jsbIndentSizeDesc'),
-      manual: gcli.lookup('jsbIndentSizeManual'),
-      defaultValue: 2
-    },
-    {
-      name: 'indentChar',
-      type: {
-        name: 'selection',
-        lookup: [{name: "space", value: " "}, {name: "tab", value: "\t"}]
-      },
-      description: gcli.lookup('jsbIndentCharDesc'),
-      manual: gcli.lookup('jsbIndentCharManual'),
-      defaultValue: ' ',
-    },
-    {
-      name: 'preserveNewlines',
-      type: 'boolean',
-      description: gcli.lookup('jsbPreserveNewlinesDesc'),
-      manual: gcli.lookup('jsbPreserveNewlinesManual'),
-      defaultValue: true
-    },
-    {
-      name: 'preserveMaxNewlines',
-      type: 'number',
-      description: gcli.lookup('jsbPreserveMaxNewlinesDesc'),
-      manual: gcli.lookup('jsbPreserveMaxNewlinesManual'),
-      defaultValue: -1
-    },
-    {
-      name: 'jslintHappy',
-      type: 'boolean',
-      description: gcli.lookup('jsbJslintHappyDesc'),
-      manual: gcli.lookup('jsbJslintHappyManual'),
-      defaultValue: false
-    },
-    {
-      name: 'braceStyle',
-      type: {
-        name: 'selection',
-        data: ['collapse', 'expand', 'end-expand', 'expand-strict']
-      },
-      description: gcli.lookup('jsbBraceStyleDesc'),
-      manual: gcli.lookup('jsbBraceStyleManual'),
-      defaultValue: "collapse"
-    },
-    {
-      name: 'spaceBeforeConditional',
-      type: 'boolean',
-      description: gcli.lookup('jsbSpaceBeforeConditionalDesc'),
-      manual: gcli.lookup('jsbSpaceBeforeConditionalManual'),
-      defaultValue: true
-    },
-    {
-      name: 'unescapeStrings',
-      type: 'boolean',
-      description: gcli.lookup('jsbUnescapeStringsDesc'),
-      manual: gcli.lookup('jsbUnescapeStringsManual'),
-      defaultValue: false
-    }
-  ],
-  exec: function(args, context) {
-  let opts = {
-    indent_size: args.indentSize,
-    indent_char: args.indentChar,
-    preserve_newlines: args.preserveNewlines,
-    max_preserve_newlines: args.preserveMaxNewlines == -1 ?
-                           undefined : args.preserveMaxNewlines,
-    jslint_happy: args.jslintHappy,
-    brace_style: args.braceStyle,
-    space_before_conditional: args.spaceBeforeConditional,
-    unescape_strings: args.unescapeStrings
-  }
-
-  let xhr = new XMLHttpRequest();
-
-  try {
-    xhr.open("GET", args.url, true);
-  } catch(e) {
-    return gcli.lookup('jsbInvalidURL');
-  }
-
-  let promise = context.createPromise();
-
-  xhr.onreadystatechange = function(aEvt) {
-    if (xhr.readyState == 4) {
-      if (xhr.status == 200 || xhr.status == 0) {
-        let browserDoc = context.environment.chromeDocument;
-        let browserWindow = browserDoc.defaultView;
-        let browser = browserWindow.gBrowser;
-
-        browser.selectedTab = browser.addTab("data:text/plain;base64," +
-          browserWindow.btoa(js_beautify(xhr.responseText, opts)));
-        promise.resolve();
-      }
-      else {
-        promise.resolve("Unable to load page to beautify: " + args.url + " " +
-                        xhr.status + " " + xhr.statusText);
-      }
-    };
-  }
-  xhr.send(null);
-  return promise;
-  }
-});
+Cu.import("resource:///modules/devtools/CmdAddon.jsm");
+Cu.import("resource:///modules/devtools/CmdBreak.jsm");
+Cu.import("resource:///modules/devtools/CmdCalllog.jsm");
+Cu.import("resource:///modules/devtools/CmdConsole.jsm");
+Cu.import("resource:///modules/devtools/CmdCookie.jsm");
+Cu.import("resource:///modules/devtools/CmdDbg.jsm");
+Cu.import("resource:///modules/devtools/CmdEcho.jsm");
+Cu.import("resource:///modules/devtools/CmdEdit.jsm");
+Cu.import("resource:///modules/devtools/CmdExport.jsm");
+Cu.import("resource:///modules/devtools/CmdInspect.jsm");
+Cu.import("resource:///modules/devtools/CmdJsb.jsm");
+Cu.import("resource:///modules/devtools/CmdPagemod.jsm");
+Cu.import("resource:///modules/devtools/CmdResize.jsm");
+Cu.import("resource:///modules/devtools/CmdRestart.jsm");
+Cu.import("resource:///modules/devtools/CmdScreenshot.jsm");
+Cu.import("resource:///modules/devtools/CmdTilt.jsm");
rename from browser/devtools/commandline/gcli.css
rename to browser/devtools/commandline/commandline.css
rename from browser/devtools/commandline/gclioutput.xhtml
rename to browser/devtools/commandline/commandlineoutput.xhtml
--- a/browser/devtools/commandline/gclioutput.xhtml
+++ b/browser/devtools/commandline/commandlineoutput.xhtml
@@ -4,15 +4,15 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <link rel="stylesheet" href="chrome://global/skin/global.css" type="text/css"/>
-  <link rel="stylesheet" href="chrome://browser/content/devtools/gcli.css" type="text/css"/>
-  <link rel="stylesheet" href="chrome://browser/skin/devtools/gcli.css" type="text/css"/>
+  <link rel="stylesheet" href="chrome://browser/content/devtools/commandline.css" type="text/css"/>
+  <link rel="stylesheet" href="chrome://browser/skin/devtools/commandline.css" type="text/css"/>
 </head>
 <body class="gcli-body">
 <div id="gcli-output-root"></div>
 </body>
 </html>
rename from browser/devtools/commandline/gclitooltip.xhtml
rename to browser/devtools/commandline/commandlinetooltip.xhtml
--- a/browser/devtools/commandline/gclitooltip.xhtml
+++ b/browser/devtools/commandline/commandlinetooltip.xhtml
@@ -4,16 +4,16 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <link rel="stylesheet" href="chrome://global/skin/global.css" type="text/css"/>
-  <link rel="stylesheet" href="chrome://browser/content/devtools/gcli.css" type="text/css"/>
-  <link rel="stylesheet" href="chrome://browser/skin/devtools/gcli.css" type="text/css"/>
+  <link rel="stylesheet" href="chrome://browser/content/devtools/commandline.css" type="text/css"/>
+  <link rel="stylesheet" href="chrome://browser/skin/devtools/commandline.css" type="text/css"/>
 </head>
 <body class="gcli-body">
 <div id="gcli-tooltip-root"></div>
 <div id="gcli-tooltip-connector"></div>
 </body>
 </html>
--- a/browser/devtools/commandline/test/Makefile.in
+++ b/browser/devtools/commandline/test/Makefile.in
@@ -7,40 +7,33 @@ DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_BROWSER_FILES = \
-  browser_gcli_addon.js \
-  browser_gcli_break.js \
-  browser_gcli_calllog.js \
-  browser_gcli_commands.js \
-  browser_gcli_cookie.js \
-  browser_gcli_dbg.js \
-  browser_gcli_edit.js \
-  browser_gcli_inspect.js \
-  browser_gcli_integrate.js \
-  browser_gcli_jsb.js \
-  browser_gcli_pagemod_export.js \
-  browser_gcli_pref.js \
-  browser_gcli_responsivemode.js \
-  browser_gcli_restart.js \
-  browser_gcli_settings.js \
+  browser_dbg_cmd_break.js \
+  browser_dbg_cmd.js \
+  browser_cmd_addon.js \
+  browser_cmd_calllog.js \
+  browser_cmd_commands.js \
+  browser_cmd_cookie.js \
+  browser_cmd_integrate.js \
+  browser_cmd_jsb.js \
+  browser_cmd_pagemod_export.js \
+  browser_cmd_pref.js \
+  browser_cmd_restart.js \
+  browser_cmd_settings.js \
   browser_gcli_web.js \
   head.js \
+  helper.js \
   $(NULL)
 
 MOCHITEST_BROWSER_FILES += \
-  browser_gcli_break.html \
-  browser_gcli_inspect.html \
-  resources_dbg.html \
-  resources_inpage.js \
-  resources_inpage1.css \
-  resources_inpage2.css \
-  resources_jsb_script.js \
-  resources.html \
+  browser_dbg_cmd_break.html \
+  browser_dbg_cmd.html \
+  browser_cmd_pagemod_export.html \
+  browser_cmd_jsb_script.jsi \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
-
rename from browser/devtools/commandline/test/browser_gcli_addon.js
rename to browser/devtools/commandline/test/browser_cmd_addon.js
--- a/browser/devtools/commandline/test/browser_gcli_addon.js
+++ b/browser/devtools/commandline/test/browser_cmd_addon.js
@@ -1,43 +1,50 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the addon commands works as they should
+
 function test() {
-  DeveloperToolbarTest.test("about:blank", function GAT_test() {
-    function GAT_ready() {
-      Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false);
+  DeveloperToolbarTest.test("about:blank", [ GAT_test ]);
+}
+
+function GAT_test() {
+  Services.obs.addObserver(GAT_ready, "gcli_addon_commands_ready", false);
+}
+
+var GAT_ready = DeveloperToolbarTest.checkCalled(function() {
+  Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false);
 
-      DeveloperToolbarTest.checkInputStatus({
-        typed: "addon list dictionary",
-        status: "VALID"
-      });
-      DeveloperToolbarTest.checkInputStatus({
-        typed: "addon list extension",
-        status: "VALID"
-      });
-      DeveloperToolbarTest.checkInputStatus({
-        typed: "addon list locale",
-        status: "VALID"
-      });
-      DeveloperToolbarTest.checkInputStatus({
-        typed: "addon list plugin",
-        status: "VALID"
-      });
-      DeveloperToolbarTest.checkInputStatus({
-        typed: "addon list theme",
-        status: "VALID"
-      });
-      DeveloperToolbarTest.checkInputStatus({
-        typed: "addon list all",
-        status: "VALID"
-      });
-      DeveloperToolbarTest.checkInputStatus({
-        typed: "addon disable Test_Plug-in_1.0.0.0",
-        status: "VALID"
-      });
-      DeveloperToolbarTest.checkInputStatus({
-        typed: "addon enable Test_Plug-in_1.0.0.0",
-        status: "VALID"
-      });
-      DeveloperToolbarTest.exec({ completed: false });
-      finish();
-    }
-    Services.obs.addObserver(GAT_ready, "gcli_addon_commands_ready", false);
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "addon list dictionary",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "addon list extension",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "addon list locale",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "addon list plugin",
+    status: "VALID"
   });
-}
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "addon list theme",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "addon list all",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "addon disable Test_Plug-in_1.0.0.0",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "addon enable Test_Plug-in_1.0.0.0",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.exec({ completed: false });
+});
rename from browser/devtools/commandline/test/browser_gcli_calllog.js
rename to browser/devtools/commandline/test/browser_cmd_calllog.js
--- a/browser/devtools/commandline/test/browser_gcli_calllog.js
+++ b/browser/devtools/commandline/test/browser_cmd_calllog.js
@@ -4,21 +4,17 @@
 // Tests that the calllog commands works as they should
 
 let imported = {};
 Components.utils.import("resource:///modules/HUDService.jsm", imported);
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-calllog";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    testCallLogStatus();
-    testCallLogExec();
-    finish();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [ testCallLogStatus, testCallLogExec ]);
 }
 
 function testCallLogStatus() {
   DeveloperToolbarTest.checkInputStatus({
     typed: "calllog",
     status: "ERROR"
   });
 
rename from browser/devtools/commandline/test/browser_gcli_commands.js
rename to browser/devtools/commandline/test/browser_cmd_commands.js
--- a/browser/devtools/commandline/test/browser_gcli_commands.js
+++ b/browser/devtools/commandline/test/browser_cmd_commands.js
@@ -4,36 +4,30 @@
 // Test various GCLI commands
 
 let imported = {};
 Components.utils.import("resource:///modules/HUDService.jsm", imported);
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-commands";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    testEcho();
-    testConsole(tab);
-
-    imported = undefined;
-    finish();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [ testEcho, testConsole ]);
 }
 
 function testEcho() {
   /*
   DeveloperToolbarTest.exec({
     typed: "echo message",
     args: { message: "message" },
     outputMatch: /^message$/,
   });
   */
 }
 
-function testConsole(tab) {
+function testConsole(browser, tab) {
   let hud = null;
   function onWebConsoleOpen(aSubject) {
     Services.obs.removeObserver(onWebConsoleOpen, "web-console-created");
 
     aSubject.QueryInterface(Ci.nsISupportsString);
     hud = imported.HUDService.getHudReferenceById(aSubject.data);
     ok(hud.hudId in imported.HUDService.hudReferences, "console open");
 
@@ -65,11 +59,10 @@ function testConsole(tab) {
       typed: "console close",
       args: {},
       blankOutput: true,
     });
 
     ok(!(hud.hudId in imported.HUDService.hudReferences), "console closed");
 
     imported = undefined;
-    finish();
   }
 }
rename from browser/devtools/commandline/test/browser_gcli_cookie.js
rename to browser/devtools/commandline/test/browser_cmd_cookie.js
--- a/browser/devtools/commandline/test/browser_gcli_cookie.js
+++ b/browser/devtools/commandline/test/browser_cmd_cookie.js
@@ -1,20 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the cookie commands works as they should
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-cookie";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    testCookieCommands();
-    finish();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [ testCookieCommands ]);
 }
 
 function testCookieCommands() {
   DeveloperToolbarTest.checkInputStatus({
     typed: "cook",
     directTabText: "ie",
     status: "ERROR"
   });
rename from browser/devtools/commandline/test/browser_gcli_integrate.js
rename to browser/devtools/commandline/test/browser_cmd_integrate.js
rename from browser/devtools/commandline/test/browser_gcli_jsb.js
rename to browser/devtools/commandline/test/browser_cmd_jsb.js
--- a/browser/devtools/commandline/test/browser_gcli_jsb.js
+++ b/browser/devtools/commandline/test/browser_cmd_jsb.js
@@ -1,48 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the jsb command works as it should
+
+const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
+                 "test/browser_cmd_jsb_script.jsi";
+
 function test() {
-  const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
-                   "test/resources_jsb_script.js";
+  DeveloperToolbarTest.test("about:blank", [ /*GJT_test*/ ]);
+}
 
-  DeveloperToolbarTest.test("about:blank", function GJT_test() {
-    /* Commented out by bug 774057, re-enable with un-hidden jsb command
-    DeveloperToolbarTest.exec({
-      typed: "jsb AAA",
-      outputMatch: /valid/
-    });
+function GJT_test() {
+  DeveloperToolbarTest.exec({
+    typed: "jsb AAA",
+    outputMatch: /valid/
+  });
 
-    gBrowser.addTabsProgressListener({
-      onProgressChange: function GJT_onProgressChange(aBrowser) {
-        gBrowser.removeTabsProgressListener(this);
+  gBrowser.addTabsProgressListener({
+    onProgressChange: DeveloperToolbarTest.checkCalled(function GJT_onProgressChange(aBrowser) {
+      gBrowser.removeTabsProgressListener(this);
 
-        let win = aBrowser._contentWindow;
-        let uri = win.document.location.href;
-        let result = win.atob(uri.replace(/.*,/, ""));
-
-        result = result.replace(/[\r\n]]/g, "\n");
+      let win = aBrowser._contentWindow;
+      let uri = win.document.location.href;
+      let result = win.atob(uri.replace(/.*,/, ""));
 
-        checkResult(result);
-        finish();
-      }
-    });
+      result = result.replace(/[\r\n]]/g, "\n");
+
+      checkResult(result);
+    })
+  });
 
-    info("Checking beautification");
-    DeveloperToolbarTest.checkInputStatus({
-      typed: "jsb " + TEST_URI + " 4 space true -1 false collapse true false",
-      status: "VALID"
-    });
-    DeveloperToolbarTest.exec({ completed: false });
+  info("Checking beautification");
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "jsb " + TEST_URI + " 4 space true -1 false collapse true false",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.exec({ completed: false });
 
-    function checkResult(aResult) {
-      let correct = "function somefunc() {\n" +
-                    "    for (let n = 0; n < 500; n++) {\n" +
-                    "        if (n % 2 == 1) {\n" +
-                    "            console.log(n);\n" +
-                    "            console.log(n + 1);\n" +
-                    "        }\n" +
-                    "    }\n" +
-                    "}";
-      is(aResult, correct, "JS has been correctly prettified");
-    }
-    */
-    finish();
-  });
+  function checkResult(aResult) {
+    let correct = "function somefunc() {\n" +
+                  "    for (let n = 0; n < 500; n++) {\n" +
+                  "        if (n % 2 == 1) {\n" +
+                  "            console.log(n);\n" +
+                  "            console.log(n + 1);\n" +
+                  "        }\n" +
+                  "    }\n" +
+                  "}";
+    is(aResult, correct, "JS has been correctly prettified");
+  }
 }
rename from browser/devtools/commandline/test/resources_jsb_script.js
rename to browser/devtools/commandline/test/browser_cmd_jsb_script.jsi
rename from browser/devtools/commandline/test/browser_gcli_inspect.html
rename to browser/devtools/commandline/test/browser_cmd_pagemod_export.html
rename from browser/devtools/commandline/test/browser_gcli_pagemod_export.js
rename to browser/devtools/commandline/test/browser_cmd_pagemod_export.js
--- a/browser/devtools/commandline/test/browser_gcli_pagemod_export.js
+++ b/browser/devtools/commandline/test/browser_cmd_pagemod_export.js
@@ -1,27 +1,30 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the inspect command works as it should
 
-const TEST_URI = "http://example.com/browser/browser/devtools/commandline/test/browser_gcli_inspect.html";
+const TEST_URI = "http://example.com/browser/browser/devtools/commandline/"+
+                 "test/browser_cmd_pagemod_export.html";
 
 function test() {
   let initialHtml = "";
 
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    initialHtml = content.document.documentElement.innerHTML;
+  DeveloperToolbarTest.test(TEST_URI, [
+    init,
+    testExportHtml,
+    testPageModReplace,
+    testPageModRemoveElement,
+    testPageModRemoveAttribute
+  ]);
 
-    testExportHtml();
-    testPageModReplace();
-    testPageModRemoveElement();
-    testPageModRemoveAttribute();
-    finish();
-  });
+  function init() {
+    initialHtml = content.document.documentElement.innerHTML;
+  }
 
   function testExportHtml() {
     DeveloperToolbarTest.checkInputStatus({
       typed: "export html",
       status: "VALID"
     });
 
     let oldOpen = content.open;
rename from browser/devtools/commandline/test/browser_gcli_pref.js
rename to browser/devtools/commandline/test/browser_cmd_pref.js
--- a/browser/devtools/commandline/test/browser_gcli_pref.js
+++ b/browser/devtools/commandline/test/browser_cmd_pref.js
@@ -17,29 +17,26 @@ imports.XPCOMUtils.defineLazyGetter(impo
 imports.XPCOMUtils.defineLazyGetter(imports, "supportsString", function() {
   return Components.classes["@mozilla.org/supports-string;1"]
           .createInstance(Components.interfaces.nsISupportsString);
 });
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-pref";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    setup();
-
-    testPrefSetEnable();
-    testPrefStatus();
-    testPrefBoolExec();
-    testPrefNumberExec();
-    testPrefStringExec();
-    testPrefSetDisable();
-
-    shutdown();
-    finish();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [
+    setup,
+    testPrefSetEnable,
+    testPrefStatus,
+    testPrefBoolExec,
+    testPrefNumberExec,
+    testPrefStringExec,
+    testPrefSetDisable,
+    shutdown
+  ]);
 }
 
 let tiltEnabledOrig = undefined;
 let tabSizeOrig = undefined;
 let remoteHostOrig = undefined;
 
 function setup() {
   Components.utils.import("resource://gre/modules/devtools/Require.jsm", imports);
rename from browser/devtools/commandline/test/browser_gcli_restart.js
rename to browser/devtools/commandline/test/browser_cmd_restart.js
--- a/browser/devtools/commandline/test/browser_gcli_restart.js
+++ b/browser/devtools/commandline/test/browser_cmd_restart.js
@@ -1,20 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that restart command works properly (input wise)
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-command-restart";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    testRestart();
-    finish();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [ testRestart ]);
 }
 
 function testRestart() {
   DeveloperToolbarTest.checkInputStatus({
     typed:  "restart",
     markup: "VVVVVVV",
     status: "VALID",
     emptyParameters: [ " [nocache]" ],
rename from browser/devtools/commandline/test/browser_gcli_settings.js
rename to browser/devtools/commandline/test/browser_cmd_settings.js
--- a/browser/devtools/commandline/test/browser_gcli_settings.js
+++ b/browser/devtools/commandline/test/browser_cmd_settings.js
@@ -17,24 +17,17 @@ imports.XPCOMUtils.defineLazyGetter(impo
 imports.XPCOMUtils.defineLazyGetter(imports, "supportsString", function() {
   return Components.classes["@mozilla.org/supports-string;1"]
           .createInstance(Components.interfaces.nsISupportsString);
 });
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-settings";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    setup();
-
-    testSettings();
-
-    shutdown();
-    finish();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [ setup, testSettings, shutdown ]);
 }
 
 let tiltEnabled = undefined;
 let tabSize = undefined;
 let remoteHost = undefined;
 
 let tiltEnabledOrig = undefined;
 let tabSizeOrig = undefined;
rename from browser/devtools/commandline/test/resources_dbg.html
rename to browser/devtools/commandline/test/browser_dbg_cmd.html
rename from browser/devtools/commandline/test/browser_gcli_dbg.js
rename to browser/devtools/commandline/test/browser_dbg_cmd.js
--- a/browser/devtools/commandline/test/browser_gcli_dbg.js
+++ b/browser/devtools/commandline/test/browser_dbg_cmd.js
@@ -1,67 +1,72 @@
 function test() {
-  const TEST_URI = TEST_BASE_HTTP + "resources_dbg.html";
+  const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
+                   "test/browser_dbg_cmd.html";
 
-  DeveloperToolbarTest.test(TEST_URI, function GAT_test() {
-    let pane = DebuggerUI.toggleDebugger();
-    ok(pane, "toggleDebugger() should return a pane.");
-    let frame = pane._frame;
+  DeveloperToolbarTest.test(TEST_URI, function() {
+    testDbgCmd();
+  });
+}
 
-    frame.addEventListener("Debugger:Connecting", function dbgConnected(aEvent) {
-      frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
+function testDbgCmd() {
+  let pane = DebuggerUI.toggleDebugger();
+  ok(pane, "toggleDebugger() should return a pane.");
+  let frame = pane._frame;
+
+  frame.addEventListener("Debugger:Connecting", function dbgConnected(aEvent) {
+    frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
 
-      // Wait for the initial resume...
-      aEvent.target.ownerDocument.defaultView.gClient
-          .addOneTimeListener("resumed", function() {
+    // Wait for the initial resume...
+    aEvent.target.ownerDocument.defaultView.gClient
+        .addOneTimeListener("resumed", function() {
 
-        info("Starting tests.");
+      info("Starting tests.");
 
-        let contentDoc = content.window.document;
-        let output = contentDoc.querySelector("input[type=text]");
-        let btnDoit = contentDoc.querySelector("input[type=button]");
+      let contentDoc = content.window.document;
+      let output = contentDoc.querySelector("input[type=text]");
+      let btnDoit = contentDoc.querySelector("input[type=button]");
 
-        cmd("dbg interrupt", function() {
-          ok(true, "debugger is paused");
-          pane.contentWindow.gClient.addOneTimeListener("resumed", function() {
-            ok(true, "debugger continued");
-            pane.contentWindow.gClient.addOneTimeListener("paused", function() {
+      cmd("dbg interrupt", function() {
+        ok(true, "debugger is paused");
+        pane.contentWindow.gClient.addOneTimeListener("resumed", function() {
+          ok(true, "debugger continued");
+          pane.contentWindow.gClient.addOneTimeListener("paused", function() {
+            cmd("dbg step in", function() {
               cmd("dbg step in", function() {
                 cmd("dbg step in", function() {
-                  cmd("dbg step in", function() {
-                    is(output.value, "step in", "debugger stepped in");
-                    cmd("dbg step over", function() {
-                      is(output.value, "step over", "debugger stepped over");
-                      cmd("dbg step out", function() {
-                        is(output.value, "step out", "debugger stepped out");
+                  is(output.value, "step in", "debugger stepped in");
+                  cmd("dbg step over", function() {
+                    is(output.value, "step over", "debugger stepped over");
+                    cmd("dbg step out", function() {
+                      is(output.value, "step out", "debugger stepped out");
+                      cmd("dbg continue", function() {
                         cmd("dbg continue", function() {
-                          cmd("dbg continue", function() {
-                            is(output.value, "dbg continue", "debugger continued");
-                            pane.contentWindow.gClient.close(function() {
-                              finish();
-                            });
+                          is(output.value, "dbg continue", "debugger continued");
+                          pane.contentWindow.gClient.close(function() {
+                            finish();
                           });
                         });
                       });
                     });
                   });
                 });
               });
             });
-            EventUtils.sendMouseEvent({type:"click"}, btnDoit);
           });
-          DeveloperToolbarTest.exec({
-            typed: "dbg continue",
-            blankOutput: true
-          });
+          EventUtils.sendMouseEvent({type:"click"}, btnDoit);
+        });
+        DeveloperToolbarTest.exec({
+          typed: "dbg continue",
+          blankOutput: true
         });
       });
+    });
 
-      function cmd(aTyped, aCallback) {
-        pane.contentWindow.gClient.addOneTimeListener("paused", aCallback);
-        DeveloperToolbarTest.exec({
-          typed: aTyped,
-          blankOutput: true
-        });
-      }
-    });
+    function cmd(aTyped, aCallback) {
+      pane.contentWindow.gClient.addOneTimeListener("paused", aCallback);
+      DeveloperToolbarTest.exec({
+        typed: aTyped,
+        blankOutput: true
+      });
+    }
   });
 }
rename from browser/devtools/commandline/test/browser_gcli_break.html
rename to browser/devtools/commandline/test/browser_dbg_cmd_break.html
rename from browser/devtools/commandline/test/browser_gcli_break.js
rename to browser/devtools/commandline/test/browser_dbg_cmd_break.js
--- a/browser/devtools/commandline/test/browser_gcli_break.js
+++ b/browser/devtools/commandline/test/browser_dbg_cmd_break.js
@@ -1,22 +1,30 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the break command works as it should
 
-const TEST_URI = "http://example.com/browser/browser/devtools/commandline/test/browser_gcli_break.html";
+const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
+                 "test/browser_dbg_cmd_break.html";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    testBreakCommands();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [ testBreakCommands ]);
 }
 
 function testBreakCommands() {
+
+  info('###################################################');
+  info('###################################################');
+  info('###################################################');
+  info('###################################################');
+  info('###################################################');
+  info('###################################################');
+  info(content.document.documentElement.innerHTML + '\n');
+
   DeveloperToolbarTest.checkInputStatus({
     typed: "brea",
     directTabText: "k",
     status: "ERROR"
   });
 
   DeveloperToolbarTest.checkInputStatus({
     typed: "break",
@@ -30,23 +38,26 @@ function testBreakCommands() {
 
   DeveloperToolbarTest.checkInputStatus({
     typed: "break add line",
     emptyParameters: [ " <file>", " <line>" ],
     status: "ERROR"
   });
 
   let pane = DebuggerUI.toggleDebugger();
-  pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
+
+  var dbgConnected = DeveloperToolbarTest.checkCalled(function() {
     pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
 
     // Wait for the initial resume.
     let client = pane.contentWindow.gClient;
-    client.addOneTimeListener("resumed", function() {
-      client.activeThread.addOneTimeListener("framesadded", function() {
+
+    var resumed = DeveloperToolbarTest.checkCalled(function() {
+
+      var framesAdded = DeveloperToolbarTest.checkCalled(function() {
         DeveloperToolbarTest.checkInputStatus({
           typed: "break add line " + TEST_URI + " " + content.wrappedJSObject.line0,
           status: "VALID"
         });
         DeveloperToolbarTest.exec({
           args: {
             type: 'line',
             file: TEST_URI,
@@ -56,27 +67,33 @@ function testBreakCommands() {
         });
 
         DeveloperToolbarTest.checkInputStatus({
           typed: "break list",
           status: "VALID"
         });
         DeveloperToolbarTest.exec();
 
-        client.activeThread.resume(function() {
+        var cleanup = DeveloperToolbarTest.checkCalled(function() {
           DeveloperToolbarTest.checkInputStatus({
             typed: "break del 0",
             status: "VALID"
           });
           DeveloperToolbarTest.exec({
             args: { breakid: 0 },
             completed: false
           });
+        });
 
-          finish();
-        });
+        client.activeThread.resume(cleanup);
       });
 
+      client.activeThread.addOneTimeListener("framesadded", framesAdded);
+
       // Trigger newScript notifications using eval.
       content.wrappedJSObject.firstCall();
     });
-  }, true);
+
+    client.addOneTimeListener("resumed", resumed);
+  });
+
+  pane._frame.addEventListener("Debugger:Connecting", dbgConnected, true);
 }
--- a/browser/devtools/commandline/test/head.js
+++ b/browser/devtools/commandline/test/head.js
@@ -6,16 +6,20 @@ const TEST_BASE_HTTP = "http://example.c
 const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/commandline/test/";
 
 let console = (function() {
   let tempScope = {};
   Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
   return tempScope.console;
 })();
 
+// Import the GCLI test helper
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
+
 /**
  * Open a new tab at a URL and call a callback on load
  */
 function addTab(aURL, aCallback)
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
@@ -34,523 +38,8 @@ function addTab(aURL, aCallback)
 
 registerCleanupFunction(function tearDown() {
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 
   console = undefined;
 });
-
-/**
- * Various functions for testing DeveloperToolbar.
- * Parts of this code exist in:
- * - browser/devtools/commandline/test/head.js
- * - browser/devtools/shared/test/head.js
- */
-let DeveloperToolbarTest = { };
-
-/**
- * Paranoid DeveloperToolbar.show();
- */
-DeveloperToolbarTest.show = function DTT_show(aCallback) {
-  if (DeveloperToolbar.visible) {
-    ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
-  }
-  else {
-    DeveloperToolbar.show(true, aCallback);
-  }
-};
-
-/**
- * Paranoid DeveloperToolbar.hide();
- */
-DeveloperToolbarTest.hide = function DTT_hide() {
-  if (!DeveloperToolbar.visible) {
-    ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
-  }
-  else {
-    DeveloperToolbar.display.inputter.setInput("");
-    DeveloperToolbar.hide();
-  }
-};
-
-/**
- * check() is the new status. Similar API except that it doesn't attempt to
- * alter the display/requisition at all, and it makes extra checks.
- * Test inputs
- *   typed: The text to type at the input
- * Available checks:
- *   input: The text displayed in the input field
- *   cursor: The position of the start of the cursor
- *   status: One of "VALID", "ERROR", "INCOMPLETE"
- *   emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
- *   directTabText: Simple completion text
- *   arrowTabText: When the completion is not an extension (without arrow)
- *   markup: What state should the error markup be in. e.g. "VVVIIIEEE"
- *   args: Maps of checks to make against the arguments:
- *     value: i.e. assignment.value (which ignores defaultValue)
- *     type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
- *           Care should be taken with this since it's something of an
- *           implementation detail
- *     arg: The toString value of the argument
- *     status: i.e. assignment.getStatus
- *     message: i.e. assignment.getMessage
- *     name: For commands - checks assignment.value.name
- */
-DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
-  if (!checks.emptyParameters) {
-    checks.emptyParameters = [];
-  }
-  if (!checks.directTabText) {
-    checks.directTabText = '';
-  }
-  if (!checks.arrowTabText) {
-    checks.arrowTabText = '';
-  }
-
-  var display = DeveloperToolbar.display;
-
-  if (checks.typed) {
-    display.inputter.setInput(checks.typed);
-  }
-  else {
-    ok(false, "Missing typed for " + JSON.stringify(checks));
-    return;
-  }
-
-  if (checks.cursor) {
-    display.inputter.setCursor(checks.cursor)
-  }
-
-  var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
-
-  var requisition = display.requisition;
-  var completer = display.completer;
-  var actual = completer._getCompleterTemplateData();
-
-  /*
-  if (checks.input) {
-    is(display.inputter.element.value,
-            checks.input,
-            'input');
-  }
-
-  if (checks.cursor) {
-    is(display.inputter.element.selectionStart,
-            checks.cursor,
-            'cursor');
-  }
-  */
-
-  if (checks.status) {
-    is(requisition.getStatus().toString(),
-            checks.status,
-            'status');
-  }
-
-  if (checks.markup) {
-    var statusMarkup = requisition.getInputStatusMarkup(cursor);
-    var actualMarkup = statusMarkup.map(function(s) {
-      return Array(s.string.length + 1).join(s.status.toString()[0]);
-    }).join('');
-
-    is(checks.markup,
-            actualMarkup,
-            'markup');
-  }
-
-  if (checks.emptyParameters) {
-    var actualParams = actual.emptyParameters;
-    is(actualParams.length,
-            checks.emptyParameters.length,
-            'emptyParameters.length');
-
-    if (actualParams.length === checks.emptyParameters.length) {
-      for (var i = 0; i < actualParams.length; i++) {
-        is(actualParams[i].replace(/\u00a0/g, ' '),
-                checks.emptyParameters[i],
-                'emptyParameters[' + i + ']');
-      }
-    }
-  }
-
-  if (checks.directTabText) {
-    is(actual.directTabText,
-            checks.directTabText,
-            'directTabText');
-  }
-
-  if (checks.arrowTabText) {
-    is(actual.arrowTabText,
-            ' \u00a0\u21E5 ' + checks.arrowTabText,
-            'arrowTabText');
-  }
-
-  if (checks.args) {
-    Object.keys(checks.args).forEach(function(paramName) {
-      var check = checks.args[paramName];
-
-      var assignment;
-      if (paramName === 'command') {
-        assignment = requisition.commandAssignment;
-      }
-      else {
-        assignment = requisition.getAssignment(paramName);
-      }
-
-      if (assignment == null) {
-        ok(false, 'Unknown parameter: ' + paramName);
-        return;
-      }
-
-      if (check.value) {
-        is(assignment.value,
-                check.value,
-                'checkStatus value for ' + paramName);
-      }
-
-      if (check.name) {
-        is(assignment.value.name,
-                check.name,
-                'checkStatus name for ' + paramName);
-      }
-
-      if (check.type) {
-        is(assignment.arg.type,
-                check.type,
-                'checkStatus type for ' + paramName);
-      }
-
-      if (check.arg) {
-        is(assignment.arg.toString(),
-                check.arg,
-                'checkStatus arg for ' + paramName);
-      }
-
-      if (check.status) {
-        is(assignment.getStatus().toString(),
-                check.status,
-                'checkStatus status for ' + paramName);
-      }
-
-      if (check.message) {
-        is(assignment.getMessage(),
-                check.message,
-                'checkStatus message for ' + paramName);
-      }
-    });
-  }
-};
-
-/**
- * Execute a command:
- *
- * DeveloperToolbarTest.exec({
- *   // Test inputs
- *   typed: "echo hi",        // Optional, uses existing if undefined
- *
- *   // Thing to check
- *   args: { message: "hi" }, // Check that the args were understood properly
- *   outputMatch: /^hi$/,     // RegExp to test against textContent of output
- *                            // (can also be array of RegExps)
- *   blankOutput: true,       // Special checks when there is no output
- * });
- */
-DeveloperToolbarTest.exec = function DTT_exec(tests) {
-  tests = tests || {};
-
-  if (tests.typed) {
-    DeveloperToolbar.display.inputter.setInput(tests.typed);
-  }
-
-  let typed = DeveloperToolbar.display.inputter.getInputState().typed;
-  let output = DeveloperToolbar.display.requisition.exec();
-
-  is(typed, output.typed, 'output.command for: ' + typed);
-
-  if (tests.completed !== false) {
-    ok(output.completed, 'output.completed false for: ' + typed);
-  }
-  else {
-    // It is actually an error if we say something is async and it turns
-    // out not to be? For now we're saying 'no'
-    // ok(!output.completed, 'output.completed true for: ' + typed);
-  }
-
-  if (tests.args != null) {
-    is(Object.keys(tests.args).length, Object.keys(output.args).length,
-       'arg count for ' + typed);
-
-    Object.keys(output.args).forEach(function(arg) {
-      let expectedArg = tests.args[arg];
-      let actualArg = output.args[arg];
-
-      if (typeof expectedArg === 'function') {
-        ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
-      }
-      else {
-        if (Array.isArray(expectedArg)) {
-          if (!Array.isArray(actualArg)) {
-            ok(false, 'actual is not an array. ' + typed + '/' + arg);
-            return;
-          }
-
-          is(expectedArg.length, actualArg.length,
-                  'array length: ' + typed + '/' + arg);
-          for (let i = 0; i < expectedArg.length; i++) {
-            is(expectedArg[i], actualArg[i],
-                    'member: "' + typed + '/' + arg + '/' + i);
-          }
-        }
-        else {
-          is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
-        }
-      }
-    });
-  }
-
-  let displayed = DeveloperToolbar.outputPanel._div.textContent;
-
-  if (tests.outputMatch) {
-    function doTest(match, against) {
-      if (!match.test(against)) {
-        ok(false, "html output for " + typed + " against " + match.source +
-                " (textContent sent to info)");
-        info("Actual textContent");
-        info(against);
-      }
-    }
-    if (Array.isArray(tests.outputMatch)) {
-      tests.outputMatch.forEach(function(match) {
-        doTest(match, displayed);
-      });
-    }
-    else {
-      doTest(tests.outputMatch, displayed);
-    }
-  }
-
-  if (tests.blankOutput != null) {
-    if (!/^$/.test(displayed)) {
-      ok(false, "html output for " + typed + " (textContent sent to info)");
-      info("Actual textContent");
-      info(displayed);
-    }
-  }
-};
-
-/**
- * Quick wrapper around the things you need to do to run DeveloperToolbar
- * command tests:
- * - Set the pref 'devtools.toolbar.enabled' to true
- * - Add a tab pointing at |uri|
- * - Open the DeveloperToolbar
- * - Register a cleanup function to undo the above
- * - Run the tests
- *
- * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
- * @param testFunc A function containing the tests to run. This should
- * arrange for 'finish()' to be called on completion.
- */
-DeveloperToolbarTest.test = function DTT_test(uri, testFunc) {
-  let menuItem = document.getElementById("menu_devToolbar");
-  let command = document.getElementById("Tools:DevToolbar");
-  let appMenuItem = document.getElementById("appmenu_devToolbar");
-
-  registerCleanupFunction(function() {
-    DeveloperToolbarTest.hide();
-
-    // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
-    if (menuItem) {
-      menuItem.hidden = true;
-    }
-    if (command) {
-      command.setAttribute("disabled", "true");
-    }
-    if (appMenuItem) {
-      appMenuItem.hidden = true;
-    }
-
-    // leakHunt({ DeveloperToolbar: DeveloperToolbar });
-  });
-
-  // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
-  if (menuItem) {
-    menuItem.hidden = false;
-  }
-  if (command) {
-    command.removeAttribute("disabled");
-  }
-  if (appMenuItem) {
-    appMenuItem.hidden = false;
-  }
-
-  addTab(uri, function(browser, tab) {
-    DeveloperToolbarTest.show(function() {
-
-      try {
-        testFunc(browser, tab);
-      }
-      catch (ex) {
-        ok(false, "" + ex);
-        console.error(ex);
-        finish();
-        throw ex;
-      }
-    });
-  });
-};
-
-
-/**
- * Memory leak hunter. Walks a tree of objects looking for DOM nodes.
- * Usage:
- * leakHunt({
- *   thing: thing,
- *   otherthing: otherthing
- * });
- */
-
-var noRecurse = [
-  /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/,
-  /^Window$/, /^Document$/,
-  /^XULDocument$/, /^XULElement$/,
-  /^DOMWindow$/, /^HTMLDocument$/, /^HTML.*Element$/
-];
-
-var hide = [ /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/ ];
-
-function leakHunt(root, path, seen) {
-  path = path || [];
-  seen = seen || [];
-
-  try {
-    var output = leakHuntInner(root, path, seen);
-    output.forEach(function(line) {
-      dump(line + '\n');
-    });
-  }
-  catch (ex) {
-    dump(ex + '\n');
-  }
-}
-
-function leakHuntInner(root, path, seen) {
-  var prefix = new Array(path.length).join('  ');
-
-  var reply = [];
-  function log(msg) {
-    reply.push(msg);
-  }
-
-  var direct
-  try {
-    direct = Object.keys(root);
-  }
-  catch (ex) {
-    log(prefix + '  Error enumerating: ' + ex);
-    return reply;
-  }
-
-  for (var prop in root) {
-    var newPath = path.slice();
-    newPath.push(prop);
-    prefix = new Array(newPath.length).join('  ');
-
-    var data;
-    try {
-      data = root[prop];
-    }
-    catch (ex) {
-      log(prefix + prop + '  Error reading: ' + ex);
-      continue;
-    }
-
-    var recurse = true;
-    var message = getType(data);
-
-    if (matchesAnyPattern(message, hide)) {
-      continue;
-    }
-
-    if (message === 'function' && direct.indexOf(prop) == -1) {
-      continue;
-    }
-
-    if (message === 'string') {
-      var extra = data.length > 10 ? data.substring(0, 9) + '_' : data;
-      message += ' "' + extra.replace(/\n/g, "|") + '"';
-      recurse = false;
-    }
-    else if (matchesAnyPattern(message, noRecurse)) {
-      message += ' (no recurse)'
-      recurse = false;
-    }
-    else if (seen.indexOf(data) !== -1) {
-      message += ' (already seen)';
-      recurse = false;
-    }
-
-    if (recurse) {
-      seen.push(data);
-      var lines = leakHuntInner(data, newPath, seen);
-      if (lines.length == 0) {
-        if (message !== 'function') {
-          log(prefix + prop + ' = ' + message + ' { }');
-        }
-      }
-      else {
-        log(prefix + prop + ' = ' + message + ' {');
-        lines.forEach(function(line) {
-          reply.push(line);
-        });
-        log(prefix + '}');
-      }
-    }
-    else {
-      log(prefix + prop + ' = ' + message);
-    }
-  }
-
-  return reply;
-}
-
-function matchesAnyPattern(str, patterns) {
-  var match = false;
-  patterns.forEach(function(pattern) {
-    if (str.match(pattern)) {
-      match = true;
-    }
-  });
-  return match;
-}
-
-function getType(data) {
-  if (data === null) {
-    return 'null';
-  }
-  if (data === undefined) {
-    return 'undefined';
-  }
-
-  var type = typeof data;
-  if (type === 'object' || type === 'Object') {
-    type = getCtorName(data);
-  }
-
-  return type;
-}
-
-function getCtorName(aObj) {
-  try {
-    if (aObj.constructor && aObj.constructor.name) {
-      return aObj.constructor.name;
-    }
-  }
-  catch (ex) {
-    return 'UnknownObject';
-  }
-
-  // If that fails, use Objects toString which sometimes gives something
-  // better than 'Object', and at least defaults to Object if nothing better
-  return Object.prototype.toString.call(aObj).slice(8, -1);
-}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/helper.js
@@ -0,0 +1,459 @@
+/* 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/. */
+
+
+/*
+ *
+ *  DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
+ *  OF THIS FILE.
+ *
+ *  UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
+ *  THE NAUGHTY STEP.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *  FOR A LONG TIME.
+ *
+ */
+
+
+/**
+ * Various functions for testing DeveloperToolbar.
+ * Parts of this code exist in:
+ * - browser/devtools/commandline/test/head.js
+ * - browser/devtools/shared/test/head.js
+ */
+let DeveloperToolbarTest = { };
+
+/**
+ * Paranoid DeveloperToolbar.show();
+ */
+DeveloperToolbarTest.show = function DTT_show(aCallback) {
+  if (DeveloperToolbar.visible) {
+    ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.show(true, aCallback);
+  }
+};
+
+/**
+ * Paranoid DeveloperToolbar.hide();
+ */
+DeveloperToolbarTest.hide = function DTT_hide() {
+  if (!DeveloperToolbar.visible) {
+    ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.display.inputter.setInput("");
+    DeveloperToolbar.hide();
+  }
+};
+
+/**
+ * check() is the new status. Similar API except that it doesn't attempt to
+ * alter the display/requisition at all, and it makes extra checks.
+ * Test inputs
+ *   typed: The text to type at the input
+ * Available checks:
+ *   input: The text displayed in the input field
+ *   cursor: The position of the start of the cursor
+ *   status: One of "VALID", "ERROR", "INCOMPLETE"
+ *   emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
+ *   directTabText: Simple completion text
+ *   arrowTabText: When the completion is not an extension (without arrow)
+ *   markup: What state should the error markup be in. e.g. "VVVIIIEEE"
+ *   args: Maps of checks to make against the arguments:
+ *     value: i.e. assignment.value (which ignores defaultValue)
+ *     type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
+ *           Care should be taken with this since it's something of an
+ *           implementation detail
+ *     arg: The toString value of the argument
+ *     status: i.e. assignment.getStatus
+ *     message: i.e. assignment.getMessage
+ *     name: For commands - checks assignment.value.name
+ */
+DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
+  if (!checks.emptyParameters) {
+    checks.emptyParameters = [];
+  }
+  if (!checks.directTabText) {
+    checks.directTabText = '';
+  }
+  if (!checks.arrowTabText) {
+    checks.arrowTabText = '';
+  }
+
+  var display = DeveloperToolbar.display;
+
+  if (checks.typed) {
+    info('Starting tests for ' + checks.typed);
+    display.inputter.setInput(checks.typed);
+  }
+  else {
+    ok(false, "Missing typed for " + JSON.stringify(checks));
+    return;
+  }
+
+  if (checks.cursor) {
+    display.inputter.setCursor(checks.cursor)
+  }
+
+  var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
+
+  var requisition = display.requisition;
+  var completer = display.completer;
+  var actual = completer._getCompleterTemplateData();
+
+  /*
+  if (checks.input) {
+    is(display.inputter.element.value,
+            checks.input,
+            'input');
+  }
+
+  if (checks.cursor) {
+    is(display.inputter.element.selectionStart,
+            checks.cursor,
+            'cursor');
+  }
+  */
+
+  if (checks.status) {
+    is(requisition.getStatus().toString(),
+            checks.status,
+            'status');
+  }
+
+  if (checks.markup) {
+    var statusMarkup = requisition.getInputStatusMarkup(cursor);
+    var actualMarkup = statusMarkup.map(function(s) {
+      return Array(s.string.length + 1).join(s.status.toString()[0]);
+    }).join('');
+
+    is(checks.markup,
+            actualMarkup,
+            'markup');
+  }
+
+  if (checks.emptyParameters) {
+    var actualParams = actual.emptyParameters;
+    is(actualParams.length,
+            checks.emptyParameters.length,
+            'emptyParameters.length');
+
+    if (actualParams.length === checks.emptyParameters.length) {
+      for (var i = 0; i < actualParams.length; i++) {
+        is(actualParams[i].replace(/\u00a0/g, ' '),
+                checks.emptyParameters[i],
+                'emptyParameters[' + i + ']');
+      }
+    }
+  }
+
+  if (checks.directTabText) {
+    is(actual.directTabText,
+            checks.directTabText,
+            'directTabText');
+  }
+
+  if (checks.arrowTabText) {
+    is(actual.arrowTabText,
+            ' \u00a0\u21E5 ' + checks.arrowTabText,
+            'arrowTabText');
+  }
+
+  if (checks.args) {
+    Object.keys(checks.args).forEach(function(paramName) {
+      var check = checks.args[paramName];
+
+      var assignment;
+      if (paramName === 'command') {
+        assignment = requisition.commandAssignment;
+      }
+      else {
+        assignment = requisition.getAssignment(paramName);
+      }
+
+      if (assignment == null) {
+        ok(false, 'Unknown parameter: ' + paramName);
+        return;
+      }
+
+      if (check.value) {
+        is(assignment.value,
+                check.value,
+                'checkStatus value for ' + paramName);
+      }
+
+      if (check.name) {
+        is(assignment.value.name,
+                check.name,
+                'checkStatus name for ' + paramName);
+      }
+
+      if (check.type) {
+        is(assignment.arg.type,
+                check.type,
+                'checkStatus type for ' + paramName);
+      }
+
+      if (check.arg) {
+        is(assignment.arg.toString(),
+                check.arg,
+                'checkStatus arg for ' + paramName);
+      }
+
+      if (check.status) {
+        is(assignment.getStatus().toString(),
+                check.status,
+                'checkStatus status for ' + paramName);
+      }
+
+      if (check.message) {
+        is(assignment.getMessage(),
+                check.message,
+                'checkStatus message for ' + paramName);
+      }
+    });
+  }
+};
+
+/**
+ * Execute a command:
+ *
+ * DeveloperToolbarTest.exec({
+ *   // Test inputs
+ *   typed: "echo hi",        // Optional, uses existing if undefined
+ *
+ *   // Thing to check
+ *   args: { message: "hi" }, // Check that the args were understood properly
+ *   outputMatch: /^hi$/,     // RegExp to test against textContent of output
+ *                            // (can also be array of RegExps)
+ *   blankOutput: true,       // Special checks when there is no output
+ * });
+ */
+DeveloperToolbarTest.exec = function DTT_exec(tests) {
+  tests = tests || {};
+
+  if (tests.typed) {
+    DeveloperToolbar.display.inputter.setInput(tests.typed);
+  }
+
+  let typed = DeveloperToolbar.display.inputter.getInputState().typed;
+  let output = DeveloperToolbar.display.requisition.exec();
+
+  is(typed, output.typed, 'output.command for: ' + typed);
+
+  if (tests.completed !== false) {
+    ok(output.completed, 'output.completed false for: ' + typed);
+  }
+  else {
+    // It is actually an error if we say something is async and it turns
+    // out not to be? For now we're saying 'no'
+    // ok(!output.completed, 'output.completed true for: ' + typed);
+  }
+
+  if (tests.args != null) {
+    is(Object.keys(tests.args).length, Object.keys(output.args).length,
+       'arg count for ' + typed);
+
+    Object.keys(output.args).forEach(function(arg) {
+      let expectedArg = tests.args[arg];
+      let actualArg = output.args[arg];
+
+      if (typeof expectedArg === 'function') {
+        ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
+      }
+      else {
+        if (Array.isArray(expectedArg)) {
+          if (!Array.isArray(actualArg)) {
+            ok(false, 'actual is not an array. ' + typed + '/' + arg);
+            return;
+          }
+
+          is(expectedArg.length, actualArg.length,
+                  'array length: ' + typed + '/' + arg);
+          for (let i = 0; i < expectedArg.length; i++) {
+            is(expectedArg[i], actualArg[i],
+                    'member: "' + typed + '/' + arg + '/' + i);
+          }
+        }
+        else {
+          is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
+        }
+      }
+    });
+  }
+
+  let displayed = DeveloperToolbar.outputPanel._div.textContent;
+
+  if (tests.outputMatch) {
+    var doTest = function(match, against) {
+      if (!match.test(against)) {
+        ok(false, "html output for " + typed + " against " + match.source +
+                " (textContent sent to info)");
+        info("Actual textContent");
+        info(against);
+      }
+    }
+    if (Array.isArray(tests.outputMatch)) {
+      tests.outputMatch.forEach(function(match) {
+        doTest(match, displayed);
+      });
+    }
+    else {
+      doTest(tests.outputMatch, displayed);
+    }
+  }
+
+  if (tests.blankOutput != null) {
+    if (!/^$/.test(displayed)) {
+      ok(false, "html output for " + typed + " (textContent sent to info)");
+      info("Actual textContent");
+      info(displayed);
+    }
+  }
+};
+
+/**
+ * Quick wrapper around the things you need to do to run DeveloperToolbar
+ * command tests:
+ * - Set the pref 'devtools.toolbar.enabled' to true
+ * - Add a tab pointing at |uri|
+ * - Open the DeveloperToolbar
+ * - Register a cleanup function to undo the above
+ * - Run the tests
+ *
+ * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
+ * @param target Either a function or array of functions containing the tests
+ * to run. If an array of test function is passed then we will clear up after
+ * the tests have completed. If a single test function is passed then this
+ * function should arrange for 'finish()' to be called on completion.
+ */
+DeveloperToolbarTest.test = function DTT_test(uri, target) {
+  let menuItem = document.getElementById("menu_devToolbar");
+  let command = document.getElementById("Tools:DevToolbar");
+  let appMenuItem = document.getElementById("appmenu_devToolbar");
+
+  registerCleanupFunction(function() {
+    DeveloperToolbarTest.hide();
+
+    // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
+    if (menuItem) {
+      menuItem.hidden = true;
+    }
+    if (command) {
+      command.setAttribute("disabled", "true");
+    }
+    if (appMenuItem) {
+      appMenuItem.hidden = true;
+    }
+
+    // leakHunt({ DeveloperToolbar: DeveloperToolbar });
+  });
+
+  // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
+  if (menuItem) {
+    menuItem.hidden = false;
+  }
+  if (command) {
+    command.removeAttribute("disabled");
+  }
+  if (appMenuItem) {
+    appMenuItem.hidden = false;
+  }
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  content.location = uri;
+
+  let tab = gBrowser.selectedTab;
+  let browser = gBrowser.getBrowserForTab(tab);
+
+  var onTabLoad = function() {
+    browser.removeEventListener("load", onTabLoad, true);
+
+    DeveloperToolbarTest.show(function() {
+      if (Array.isArray(target)) {
+        try {
+          target.forEach(function(func) {
+            func(browser, tab);
+          })
+        }
+        finally {
+          DeveloperToolbarTest._checkFinish();
+        }
+      }
+      else {
+        try {
+          target(browser, tab);
+        }
+        catch (ex) {
+          ok(false, "" + ex);
+          DeveloperToolbarTest._finish();
+          throw ex;
+        }
+      }
+    });
+  }
+
+  browser.addEventListener("load", onTabLoad, true);
+};
+
+DeveloperToolbarTest._outstanding = [];
+
+DeveloperToolbarTest._checkFinish = function() {
+  if (DeveloperToolbarTest._outstanding.length == 0) {
+    DeveloperToolbarTest._finish();
+  }
+}
+
+DeveloperToolbarTest._finish = function() {
+  DeveloperToolbarTest.closeAllTabs();
+  finish();
+}
+
+DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
+  var todo = function() {
+    var reply = aFunc.apply(aScope, arguments);
+    DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
+      return aJob != todo;
+    });
+    DeveloperToolbarTest._checkFinish();
+    return reply;
+  }
+  DeveloperToolbarTest._outstanding.push(todo);
+  return todo;
+};
+
+DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
+  return function() {
+    ok(false, aMsg);
+    return aFunc.apply(aScope, arguments);
+  }
+};
+
+/**
+ *
+ */
+DeveloperToolbarTest.closeAllTabs = function() {
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/CmdInspect.jsm
@@ -0,0 +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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+
+/**
+ * 'inspect' command
+ */
+gcli.addCommand({
+  name: "inspect",
+  description: gcli.lookup("inspectDesc"),
+  manual: gcli.lookup("inspectManual"),
+  params: [
+    {
+      name: "node",
+      type: "node",
+      description: gcli.lookup("inspectNodeDesc"),
+      manual: gcli.lookup("inspectNodeManual")
+    }
+  ],
+  exec: function Command_inspect(args, context) {
+    let document = context.environment.chromeDocument;
+    document.defaultView.InspectorUI.openInspectorUI(args.node);
+  }
+});
--- a/browser/devtools/highlighter/Makefile.in
+++ b/browser/devtools/highlighter/Makefile.in
@@ -19,8 +19,11 @@ EXTRA_JS_MODULES = \
 
 EXTRA_PP_JS_MODULES = \
 	inspector.jsm \
 	$(NULL)
 
 TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
+
+libs::
+	$(NSINSTALL) $(srcdir)/CmdInspect.jsm $(FINAL_TARGET)/modules/devtools
--- a/browser/devtools/highlighter/test/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -32,13 +32,16 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_ruleviewstore.js \
 		browser_inspector_invalidate.js \
 		browser_inspector_sidebarstate.js \
 		browser_inspector_menu.js \
 		browser_inspector_pseudoclass_lock.js \
 		browser_inspector_pseudoClass_menu.js \
 		browser_inspector_destroyselection.html \
 		browser_inspector_destroyselection.js \
+		browser_inspector_cmd_inspect.js \
+		browser_inspector_cmd_inspect.html \
 		head.js \
+		helper.js \
 		$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
copy from browser/devtools/commandline/test/browser_gcli_inspect.html
copy to browser/devtools/highlighter/test/browser_inspector_cmd_inspect.html
rename from browser/devtools/commandline/test/browser_gcli_inspect.js
rename to browser/devtools/highlighter/test/browser_inspector_cmd_inspect.js
--- a/browser/devtools/commandline/test/browser_gcli_inspect.js
+++ b/browser/devtools/highlighter/test/browser_inspector_cmd_inspect.js
@@ -1,21 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the inspect command works as it should
 
-const TEST_URI = "http://example.com/browser/browser/devtools/commandline/test/browser_gcli_inspect.html";
+const TEST_URI = "http://example.com/browser/browser/devtools/highlighter/" +
+                 "test/browser_inspector_cmd_inspect.html";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    testInspect();
-
-    finish();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [ testInspect ]);
 }
 
 function testInspect() {
   DeveloperToolbarTest.checkInputStatus({
     typed: "inspec",
     directTabText: "t",
     status: "ERROR"
   });
--- a/browser/devtools/highlighter/test/head.js
+++ b/browser/devtools/highlighter/test/head.js
@@ -2,16 +2,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cu = Components.utils;
 let tempScope = {};
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
 let LayoutHelpers = tempScope.LayoutHelpers;
 
+// Import the GCLI test helper
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
+
 // Clear preferences that may be set during the course of tests.
 function clearUserPrefs()
 {
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
 }
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/helper.js
@@ -0,0 +1,459 @@
+/* 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/. */
+
+
+/*
+ *
+ *  DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
+ *  OF THIS FILE.
+ *
+ *  UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
+ *  THE NAUGHTY STEP.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *  FOR A LONG TIME.
+ *
+ */
+
+
+/**
+ * Various functions for testing DeveloperToolbar.
+ * Parts of this code exist in:
+ * - browser/devtools/commandline/test/head.js
+ * - browser/devtools/shared/test/head.js
+ */
+let DeveloperToolbarTest = { };
+
+/**
+ * Paranoid DeveloperToolbar.show();
+ */
+DeveloperToolbarTest.show = function DTT_show(aCallback) {
+  if (DeveloperToolbar.visible) {
+    ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.show(true, aCallback);
+  }
+};
+
+/**
+ * Paranoid DeveloperToolbar.hide();
+ */
+DeveloperToolbarTest.hide = function DTT_hide() {
+  if (!DeveloperToolbar.visible) {
+    ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.display.inputter.setInput("");
+    DeveloperToolbar.hide();
+  }
+};
+
+/**
+ * check() is the new status. Similar API except that it doesn't attempt to
+ * alter the display/requisition at all, and it makes extra checks.
+ * Test inputs
+ *   typed: The text to type at the input
+ * Available checks:
+ *   input: The text displayed in the input field
+ *   cursor: The position of the start of the cursor
+ *   status: One of "VALID", "ERROR", "INCOMPLETE"
+ *   emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
+ *   directTabText: Simple completion text
+ *   arrowTabText: When the completion is not an extension (without arrow)
+ *   markup: What state should the error markup be in. e.g. "VVVIIIEEE"
+ *   args: Maps of checks to make against the arguments:
+ *     value: i.e. assignment.value (which ignores defaultValue)
+ *     type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
+ *           Care should be taken with this since it's something of an
+ *           implementation detail
+ *     arg: The toString value of the argument
+ *     status: i.e. assignment.getStatus
+ *     message: i.e. assignment.getMessage
+ *     name: For commands - checks assignment.value.name
+ */
+DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
+  if (!checks.emptyParameters) {
+    checks.emptyParameters = [];
+  }
+  if (!checks.directTabText) {
+    checks.directTabText = '';
+  }
+  if (!checks.arrowTabText) {
+    checks.arrowTabText = '';
+  }
+
+  var display = DeveloperToolbar.display;
+
+  if (checks.typed) {
+    info('Starting tests for ' + checks.typed);
+    display.inputter.setInput(checks.typed);
+  }
+  else {
+    ok(false, "Missing typed for " + JSON.stringify(checks));
+    return;
+  }
+
+  if (checks.cursor) {
+    display.inputter.setCursor(checks.cursor)
+  }
+
+  var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
+
+  var requisition = display.requisition;
+  var completer = display.completer;
+  var actual = completer._getCompleterTemplateData();
+
+  /*
+  if (checks.input) {
+    is(display.inputter.element.value,
+            checks.input,
+            'input');
+  }
+
+  if (checks.cursor) {
+    is(display.inputter.element.selectionStart,
+            checks.cursor,
+            'cursor');
+  }
+  */
+
+  if (checks.status) {
+    is(requisition.getStatus().toString(),
+            checks.status,
+            'status');
+  }
+
+  if (checks.markup) {
+    var statusMarkup = requisition.getInputStatusMarkup(cursor);
+    var actualMarkup = statusMarkup.map(function(s) {
+      return Array(s.string.length + 1).join(s.status.toString()[0]);
+    }).join('');
+
+    is(checks.markup,
+            actualMarkup,
+            'markup');
+  }
+
+  if (checks.emptyParameters) {
+    var actualParams = actual.emptyParameters;
+    is(actualParams.length,
+            checks.emptyParameters.length,
+            'emptyParameters.length');
+
+    if (actualParams.length === checks.emptyParameters.length) {
+      for (var i = 0; i < actualParams.length; i++) {
+        is(actualParams[i].replace(/\u00a0/g, ' '),
+                checks.emptyParameters[i],
+                'emptyParameters[' + i + ']');
+      }
+    }
+  }
+
+  if (checks.directTabText) {
+    is(actual.directTabText,
+            checks.directTabText,
+            'directTabText');
+  }
+
+  if (checks.arrowTabText) {
+    is(actual.arrowTabText,
+            ' \u00a0\u21E5 ' + checks.arrowTabText,
+            'arrowTabText');
+  }
+
+  if (checks.args) {
+    Object.keys(checks.args).forEach(function(paramName) {
+      var check = checks.args[paramName];
+
+      var assignment;
+      if (paramName === 'command') {
+        assignment = requisition.commandAssignment;
+      }
+      else {
+        assignment = requisition.getAssignment(paramName);
+      }
+
+      if (assignment == null) {
+        ok(false, 'Unknown parameter: ' + paramName);
+        return;
+      }
+
+      if (check.value) {
+        is(assignment.value,
+                check.value,
+                'checkStatus value for ' + paramName);
+      }
+
+      if (check.name) {
+        is(assignment.value.name,
+                check.name,
+                'checkStatus name for ' + paramName);
+      }
+
+      if (check.type) {
+        is(assignment.arg.type,
+                check.type,
+                'checkStatus type for ' + paramName);
+      }
+
+      if (check.arg) {
+        is(assignment.arg.toString(),
+                check.arg,
+                'checkStatus arg for ' + paramName);
+      }
+
+      if (check.status) {
+        is(assignment.getStatus().toString(),
+                check.status,
+                'checkStatus status for ' + paramName);
+      }
+
+      if (check.message) {
+        is(assignment.getMessage(),
+                check.message,
+                'checkStatus message for ' + paramName);
+      }
+    });
+  }
+};
+
+/**
+ * Execute a command:
+ *
+ * DeveloperToolbarTest.exec({
+ *   // Test inputs
+ *   typed: "echo hi",        // Optional, uses existing if undefined
+ *
+ *   // Thing to check
+ *   args: { message: "hi" }, // Check that the args were understood properly
+ *   outputMatch: /^hi$/,     // RegExp to test against textContent of output
+ *                            // (can also be array of RegExps)
+ *   blankOutput: true,       // Special checks when there is no output
+ * });
+ */
+DeveloperToolbarTest.exec = function DTT_exec(tests) {
+  tests = tests || {};
+
+  if (tests.typed) {
+    DeveloperToolbar.display.inputter.setInput(tests.typed);
+  }
+
+  let typed = DeveloperToolbar.display.inputter.getInputState().typed;
+  let output = DeveloperToolbar.display.requisition.exec();
+
+  is(typed, output.typed, 'output.command for: ' + typed);
+
+  if (tests.completed !== false) {
+    ok(output.completed, 'output.completed false for: ' + typed);
+  }
+  else {
+    // It is actually an error if we say something is async and it turns
+    // out not to be? For now we're saying 'no'
+    // ok(!output.completed, 'output.completed true for: ' + typed);
+  }
+
+  if (tests.args != null) {
+    is(Object.keys(tests.args).length, Object.keys(output.args).length,
+       'arg count for ' + typed);
+
+    Object.keys(output.args).forEach(function(arg) {
+      let expectedArg = tests.args[arg];
+      let actualArg = output.args[arg];
+
+      if (typeof expectedArg === 'function') {
+        ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
+      }
+      else {
+        if (Array.isArray(expectedArg)) {
+          if (!Array.isArray(actualArg)) {
+            ok(false, 'actual is not an array. ' + typed + '/' + arg);
+            return;
+          }
+
+          is(expectedArg.length, actualArg.length,
+                  'array length: ' + typed + '/' + arg);
+          for (let i = 0; i < expectedArg.length; i++) {
+            is(expectedArg[i], actualArg[i],
+                    'member: "' + typed + '/' + arg + '/' + i);
+          }
+        }
+        else {
+          is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
+        }
+      }
+    });
+  }
+
+  let displayed = DeveloperToolbar.outputPanel._div.textContent;
+
+  if (tests.outputMatch) {
+    var doTest = function(match, against) {
+      if (!match.test(against)) {
+        ok(false, "html output for " + typed + " against " + match.source +
+                " (textContent sent to info)");
+        info("Actual textContent");
+        info(against);
+      }
+    }
+    if (Array.isArray(tests.outputMatch)) {
+      tests.outputMatch.forEach(function(match) {
+        doTest(match, displayed);
+      });
+    }
+    else {
+      doTest(tests.outputMatch, displayed);
+    }
+  }
+
+  if (tests.blankOutput != null) {
+    if (!/^$/.test(displayed)) {
+      ok(false, "html output for " + typed + " (textContent sent to info)");
+      info("Actual textContent");
+      info(displayed);
+    }
+  }
+};
+
+/**
+ * Quick wrapper around the things you need to do to run DeveloperToolbar
+ * command tests:
+ * - Set the pref 'devtools.toolbar.enabled' to true
+ * - Add a tab pointing at |uri|
+ * - Open the DeveloperToolbar
+ * - Register a cleanup function to undo the above
+ * - Run the tests
+ *
+ * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
+ * @param target Either a function or array of functions containing the tests
+ * to run. If an array of test function is passed then we will clear up after
+ * the tests have completed. If a single test function is passed then this
+ * function should arrange for 'finish()' to be called on completion.
+ */
+DeveloperToolbarTest.test = function DTT_test(uri, target) {
+  let menuItem = document.getElementById("menu_devToolbar");
+  let command = document.getElementById("Tools:DevToolbar");
+  let appMenuItem = document.getElementById("appmenu_devToolbar");
+
+  registerCleanupFunction(function() {
+    DeveloperToolbarTest.hide();
+
+    // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
+    if (menuItem) {
+      menuItem.hidden = true;
+    }
+    if (command) {
+      command.setAttribute("disabled", "true");
+    }
+    if (appMenuItem) {
+      appMenuItem.hidden = true;
+    }
+
+    // leakHunt({ DeveloperToolbar: DeveloperToolbar });
+  });
+
+  // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
+  if (menuItem) {
+    menuItem.hidden = false;
+  }
+  if (command) {
+    command.removeAttribute("disabled");
+  }
+  if (appMenuItem) {
+    appMenuItem.hidden = false;
+  }
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  content.location = uri;
+
+  let tab = gBrowser.selectedTab;
+  let browser = gBrowser.getBrowserForTab(tab);
+
+  var onTabLoad = function() {
+    browser.removeEventListener("load", onTabLoad, true);
+
+    DeveloperToolbarTest.show(function() {
+      if (Array.isArray(target)) {
+        try {
+          target.forEach(function(func) {
+            func(browser, tab);
+          })
+        }
+        finally {
+          DeveloperToolbarTest._checkFinish();
+        }
+      }
+      else {
+        try {
+          target(browser, tab);
+        }
+        catch (ex) {
+          ok(false, "" + ex);
+          DeveloperToolbarTest._finish();
+          throw ex;
+        }
+      }
+    });
+  }
+
+  browser.addEventListener("load", onTabLoad, true);
+};
+
+DeveloperToolbarTest._outstanding = [];
+
+DeveloperToolbarTest._checkFinish = function() {
+  if (DeveloperToolbarTest._outstanding.length == 0) {
+    DeveloperToolbarTest._finish();
+  }
+}
+
+DeveloperToolbarTest._finish = function() {
+  DeveloperToolbarTest.closeAllTabs();
+  finish();
+}
+
+DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
+  var todo = function() {
+    var reply = aFunc.apply(aScope, arguments);
+    DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
+      return aJob != todo;
+    });
+    DeveloperToolbarTest._checkFinish();
+    return reply;
+  }
+  DeveloperToolbarTest._outstanding.push(todo);
+  return todo;
+};
+
+DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
+  return function() {
+    ok(false, aMsg);
+    return aFunc.apply(aScope, arguments);
+  }
+};
+
+/**
+ *
+ */
+DeveloperToolbarTest.closeAllTabs = function() {
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
+};
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -21,11 +21,11 @@ browser.jar:
     content/browser/devtools/layoutview/view.xhtml  (layoutview/view.xhtml)
     content/browser/devtools/layoutview/view.css  (layoutview/view.css)
     content/browser/orion.js                      (sourceeditor/orion/orion.js)
 *   content/browser/source-editor-overlay.xul     (sourceeditor/source-editor-overlay.xul)
 *   content/browser/debugger.xul                  (debugger/debugger.xul)
     content/browser/debugger.css                  (debugger/debugger.css)
     content/browser/debugger-controller.js        (debugger/debugger-controller.js)
     content/browser/debugger-view.js              (debugger/debugger-view.js)
-    content/browser/devtools/gcli.css             (commandline/gcli.css)
-    content/browser/devtools/gclioutput.xhtml     (commandline/gclioutput.xhtml)
-    content/browser/devtools/gclitooltip.xhtml    (commandline/gclitooltip.xhtml)
+    content/browser/devtools/commandline.css      (commandline/commandline.css)
+    content/browser/devtools/commandlineoutput.xhtml  (commandline/commandlineoutput.xhtml)
+    content/browser/devtools/commandlinetooltip.xhtml  (commandline/commandlinetooltip.xhtml)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/responsivedesign/CmdResize.jsm
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource:///modules/devtools/gcli.jsm");
+
+/* Responsive Mode commands */
+gcli.addCommand({
+  name: 'resize',
+  description: gcli.lookup('resizeModeDesc')
+});
+
+gcli.addCommand({
+  name: 'resize on',
+  description: gcli.lookup('resizeModeOnDesc'),
+  manual: gcli.lookup('resizeModeManual'),
+  exec: gcli_cmd_resize
+});
+
+gcli.addCommand({
+  name: 'resize off',
+  description: gcli.lookup('resizeModeOffDesc'),
+  manual: gcli.lookup('resizeModeManual'),
+  exec: gcli_cmd_resize
+});
+
+gcli.addCommand({
+  name: 'resize toggle',
+  description: gcli.lookup('resizeModeToggleDesc'),
+  manual: gcli.lookup('resizeModeManual'),
+  exec: gcli_cmd_resize
+});
+
+gcli.addCommand({
+  name: 'resize to',
+  description: gcli.lookup('resizeModeToDesc'),
+  params: [
+    {
+      name: 'width',
+      type: 'number',
+      description: gcli.lookup("resizePageArgWidthDesc"),
+    },
+    {
+      name: 'height',
+      type: 'number',
+      description: gcli.lookup("resizePageArgHeightDesc"),
+    },
+  ],
+  exec: gcli_cmd_resize
+});
+
+function gcli_cmd_resize(args, context) {
+  let browserDoc = context.environment.chromeDocument;
+  let browserWindow = browserDoc.defaultView;
+  let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager;
+  mgr.handleGcliCommand(browserWindow,
+                        browserWindow.gBrowser.selectedTab,
+                        this.name,
+                        args);
+}
--- a/browser/devtools/responsivedesign/test/Makefile.in
+++ b/browser/devtools/responsivedesign/test/Makefile.in
@@ -42,14 +42,17 @@ VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
 		browser_responsiveui.js \
 		browser_responsiveruleview.js \
+		browser_responsive_cmd.js \
 		browser_responsivecomputedview.js \
+		head.js \
+		helper.js \
 		$(NULL)
 
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
rename from browser/devtools/commandline/test/browser_gcli_responsivemode.js
rename to browser/devtools/responsivedesign/test/browser_responsive_cmd.js
--- a/browser/devtools/commandline/test/browser_gcli_responsivemode.js
+++ b/browser/devtools/responsivedesign/test/browser_responsive_cmd.js
@@ -1,58 +1,60 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
-  DeveloperToolbarTest.test("about:blank", function GAT_test() {
-    DeveloperToolbarTest.checkInputStatus({
-      typed: "resize toggle",
-      status: "VALID"
-    });
-    DeveloperToolbarTest.exec();
-    ok(isOpen(), "responsive mode is open");
+  DeveloperToolbarTest.test("about:blank", [ GAT_test ]);
+}
+
+function isOpen() {
+  return !!gBrowser.selectedTab.__responsiveUI;
+}
+
+function isClosed() {
+  return !isOpen();
+}
 
-    DeveloperToolbarTest.checkInputStatus({
-      typed: "resize toggle",
-      status: "VALID"
-    });
-    DeveloperToolbarTest.exec();
-    ok(isClosed(), "responsive mode is closed");
+function GAT_test() {
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "resize toggle",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.exec();
+  ok(isOpen(), "responsive mode is open");
 
-    DeveloperToolbarTest.checkInputStatus({
-      typed: "resize on",
-      status: "VALID"
-    });
-    DeveloperToolbarTest.exec();
-    ok(isOpen(), "responsive mode is open");
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "resize toggle",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.exec();
+  ok(isClosed(), "responsive mode is closed");
 
-    DeveloperToolbarTest.checkInputStatus({
-      typed: "resize off",
-      status: "VALID"
-    });
-    DeveloperToolbarTest.exec();
-    ok(isClosed(), "responsive mode is closed");
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "resize on",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.exec();
+  ok(isOpen(), "responsive mode is open");
 
-    DeveloperToolbarTest.checkInputStatus({
-      typed: "resize to 400 400",
-      status: "VALID"
-    });
-    DeveloperToolbarTest.exec();
-    ok(isOpen(), "responsive mode is open");
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "resize off",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.exec();
+  ok(isClosed(), "responsive mode is closed");
 
-    DeveloperToolbarTest.checkInputStatus({
-      typed: "resize off",
-      status: "VALID"
-    });
-    DeveloperToolbarTest.exec();
-    ok(isClosed(), "responsive mode is closed");
-
-    executeSoon(finish);
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "resize to 400 400",
+    status: "VALID"
   });
-
-  function isOpen() {
-    return !!gBrowser.selectedTab.__responsiveUI;
-  }
+  DeveloperToolbarTest.exec();
+  ok(isOpen(), "responsive mode is open");
 
-  function isClosed() {
-    return !isOpen();
-  }
+  DeveloperToolbarTest.checkInputStatus({
+    typed: "resize off",
+    status: "VALID"
+  });
+  DeveloperToolbarTest.exec();
+  ok(isClosed(), "responsive mode is closed");
+
+  // executeSoon(finish);
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/responsivedesign/test/head.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Import the GCLI test helper
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/responsivedesign/test/helper.js
@@ -0,0 +1,459 @@
+/* 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/. */
+
+
+/*
+ *
+ *  DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
+ *  OF THIS FILE.
+ *
+ *  UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
+ *  THE NAUGHTY STEP.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *  FOR A LONG TIME.
+ *
+ */
+
+
+/**
+ * Various functions for testing DeveloperToolbar.
+ * Parts of this code exist in:
+ * - browser/devtools/commandline/test/head.js
+ * - browser/devtools/shared/test/head.js
+ */
+let DeveloperToolbarTest = { };
+
+/**
+ * Paranoid DeveloperToolbar.show();
+ */
+DeveloperToolbarTest.show = function DTT_show(aCallback) {
+  if (DeveloperToolbar.visible) {
+    ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.show(true, aCallback);
+  }
+};
+
+/**
+ * Paranoid DeveloperToolbar.hide();
+ */
+DeveloperToolbarTest.hide = function DTT_hide() {
+  if (!DeveloperToolbar.visible) {
+    ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.display.inputter.setInput("");
+    DeveloperToolbar.hide();
+  }
+};
+
+/**
+ * check() is the new status. Similar API except that it doesn't attempt to
+ * alter the display/requisition at all, and it makes extra checks.
+ * Test inputs
+ *   typed: The text to type at the input
+ * Available checks:
+ *   input: The text displayed in the input field
+ *   cursor: The position of the start of the cursor
+ *   status: One of "VALID", "ERROR", "INCOMPLETE"
+ *   emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
+ *   directTabText: Simple completion text
+ *   arrowTabText: When the completion is not an extension (without arrow)
+ *   markup: What state should the error markup be in. e.g. "VVVIIIEEE"
+ *   args: Maps of checks to make against the arguments:
+ *     value: i.e. assignment.value (which ignores defaultValue)
+ *     type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
+ *           Care should be taken with this since it's something of an
+ *           implementation detail
+ *     arg: The toString value of the argument
+ *     status: i.e. assignment.getStatus
+ *     message: i.e. assignment.getMessage
+ *     name: For commands - checks assignment.value.name
+ */
+DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
+  if (!checks.emptyParameters) {
+    checks.emptyParameters = [];
+  }
+  if (!checks.directTabText) {
+    checks.directTabText = '';
+  }
+  if (!checks.arrowTabText) {
+    checks.arrowTabText = '';
+  }
+
+  var display = DeveloperToolbar.display;
+
+  if (checks.typed) {
+    info('Starting tests for ' + checks.typed);
+    display.inputter.setInput(checks.typed);
+  }
+  else {
+    ok(false, "Missing typed for " + JSON.stringify(checks));
+    return;
+  }
+
+  if (checks.cursor) {
+    display.inputter.setCursor(checks.cursor)
+  }
+
+  var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
+
+  var requisition = display.requisition;
+  var completer = display.completer;
+  var actual = completer._getCompleterTemplateData();
+
+  /*
+  if (checks.input) {
+    is(display.inputter.element.value,
+            checks.input,
+            'input');
+  }
+
+  if (checks.cursor) {
+    is(display.inputter.element.selectionStart,
+            checks.cursor,
+            'cursor');
+  }
+  */
+
+  if (checks.status) {
+    is(requisition.getStatus().toString(),
+            checks.status,
+            'status');
+  }
+
+  if (checks.markup) {
+    var statusMarkup = requisition.getInputStatusMarkup(cursor);
+    var actualMarkup = statusMarkup.map(function(s) {
+      return Array(s.string.length + 1).join(s.status.toString()[0]);
+    }).join('');
+
+    is(checks.markup,
+            actualMarkup,
+            'markup');
+  }
+
+  if (checks.emptyParameters) {
+    var actualParams = actual.emptyParameters;
+    is(actualParams.length,
+            checks.emptyParameters.length,
+            'emptyParameters.length');
+
+    if (actualParams.length === checks.emptyParameters.length) {
+      for (var i = 0; i < actualParams.length; i++) {
+        is(actualParams[i].replace(/\u00a0/g, ' '),
+                checks.emptyParameters[i],
+                'emptyParameters[' + i + ']');
+      }
+    }
+  }
+
+  if (checks.directTabText) {
+    is(actual.directTabText,
+            checks.directTabText,
+            'directTabText');
+  }
+
+  if (checks.arrowTabText) {
+    is(actual.arrowTabText,
+            ' \u00a0\u21E5 ' + checks.arrowTabText,
+            'arrowTabText');
+  }
+
+  if (checks.args) {
+    Object.keys(checks.args).forEach(function(paramName) {
+      var check = checks.args[paramName];
+
+      var assignment;
+      if (paramName === 'command') {
+        assignment = requisition.commandAssignment;
+      }
+      else {
+        assignment = requisition.getAssignment(paramName);
+      }
+
+      if (assignment == null) {
+        ok(false, 'Unknown parameter: ' + paramName);
+        return;
+      }
+
+      if (check.value) {
+        is(assignment.value,
+                check.value,
+                'checkStatus value for ' + paramName);
+      }
+
+      if (check.name) {
+        is(assignment.value.name,
+                check.name,
+                'checkStatus name for ' + paramName);
+      }
+
+      if (check.type) {
+        is(assignment.arg.type,
+                check.type,
+                'checkStatus type for ' + paramName);
+      }
+
+      if (check.arg) {
+        is(assignment.arg.toString(),
+                check.arg,
+                'checkStatus arg for ' + paramName);
+      }
+
+      if (check.status) {
+        is(assignment.getStatus().toString(),
+                check.status,
+                'checkStatus status for ' + paramName);
+      }
+
+      if (check.message) {
+        is(assignment.getMessage(),
+                check.message,
+                'checkStatus message for ' + paramName);
+      }
+    });
+  }
+};
+
+/**
+ * Execute a command:
+ *
+ * DeveloperToolbarTest.exec({
+ *   // Test inputs
+ *   typed: "echo hi",        // Optional, uses existing if undefined
+ *
+ *   // Thing to check
+ *   args: { message: "hi" }, // Check that the args were understood properly
+ *   outputMatch: /^hi$/,     // RegExp to test against textContent of output
+ *                            // (can also be array of RegExps)
+ *   blankOutput: true,       // Special checks when there is no output
+ * });
+ */
+DeveloperToolbarTest.exec = function DTT_exec(tests) {
+  tests = tests || {};
+
+  if (tests.typed) {
+    DeveloperToolbar.display.inputter.setInput(tests.typed);
+  }
+
+  let typed = DeveloperToolbar.display.inputter.getInputState().typed;
+  let output = DeveloperToolbar.display.requisition.exec();
+
+  is(typed, output.typed, 'output.command for: ' + typed);
+
+  if (tests.completed !== false) {
+    ok(output.completed, 'output.completed false for: ' + typed);
+  }
+  else {
+    // It is actually an error if we say something is async and it turns
+    // out not to be? For now we're saying 'no'
+    // ok(!output.completed, 'output.completed true for: ' + typed);
+  }
+
+  if (tests.args != null) {
+    is(Object.keys(tests.args).length, Object.keys(output.args).length,
+       'arg count for ' + typed);
+
+    Object.keys(output.args).forEach(function(arg) {
+      let expectedArg = tests.args[arg];
+      let actualArg = output.args[arg];
+
+      if (typeof expectedArg === 'function') {
+        ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
+      }
+      else {
+        if (Array.isArray(expectedArg)) {
+          if (!Array.isArray(actualArg)) {
+            ok(false, 'actual is not an array. ' + typed + '/' + arg);
+            return;
+          }
+
+          is(expectedArg.length, actualArg.length,
+                  'array length: ' + typed + '/' + arg);
+          for (let i = 0; i < expectedArg.length; i++) {
+            is(expectedArg[i], actualArg[i],
+                    'member: "' + typed + '/' + arg + '/' + i);
+          }
+        }
+        else {
+          is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
+        }
+      }
+    });
+  }
+
+  let displayed = DeveloperToolbar.outputPanel._div.textContent;
+
+  if (tests.outputMatch) {
+    var doTest = function(match, against) {
+      if (!match.test(against)) {
+        ok(false, "html output for " + typed + " against " + match.source +
+                " (textContent sent to info)");
+        info("Actual textContent");
+        info(against);
+      }
+    }
+    if (Array.isArray(tests.outputMatch)) {
+      tests.outputMatch.forEach(function(match) {
+        doTest(match, displayed);
+      });
+    }
+    else {
+      doTest(tests.outputMatch, displayed);
+    }
+  }
+
+  if (tests.blankOutput != null) {
+    if (!/^$/.test(displayed)) {
+      ok(false, "html output for " + typed + " (textContent sent to info)");
+      info("Actual textContent");
+      info(displayed);
+    }
+  }
+};
+
+/**
+ * Quick wrapper around the things you need to do to run DeveloperToolbar
+ * command tests:
+ * - Set the pref 'devtools.toolbar.enabled' to true
+ * - Add a tab pointing at |uri|
+ * - Open the DeveloperToolbar
+ * - Register a cleanup function to undo the above
+ * - Run the tests
+ *
+ * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
+ * @param target Either a function or array of functions containing the tests
+ * to run. If an array of test function is passed then we will clear up after
+ * the tests have completed. If a single test function is passed then this
+ * function should arrange for 'finish()' to be called on completion.
+ */
+DeveloperToolbarTest.test = function DTT_test(uri, target) {
+  let menuItem = document.getElementById("menu_devToolbar");
+  let command = document.getElementById("Tools:DevToolbar");
+  let appMenuItem = document.getElementById("appmenu_devToolbar");
+
+  registerCleanupFunction(function() {
+    DeveloperToolbarTest.hide();
+
+    // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
+    if (menuItem) {
+      menuItem.hidden = true;
+    }
+    if (command) {
+      command.setAttribute("disabled", "true");
+    }
+    if (appMenuItem) {
+      appMenuItem.hidden = true;
+    }
+
+    // leakHunt({ DeveloperToolbar: DeveloperToolbar });
+  });
+
+  // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
+  if (menuItem) {
+    menuItem.hidden = false;
+  }
+  if (command) {
+    command.removeAttribute("disabled");
+  }
+  if (appMenuItem) {
+    appMenuItem.hidden = false;
+  }
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  content.location = uri;
+
+  let tab = gBrowser.selectedTab;
+  let browser = gBrowser.getBrowserForTab(tab);
+
+  var onTabLoad = function() {
+    browser.removeEventListener("load", onTabLoad, true);
+
+    DeveloperToolbarTest.show(function() {
+      if (Array.isArray(target)) {
+        try {
+          target.forEach(function(func) {
+            func(browser, tab);
+          })
+        }
+        finally {
+          DeveloperToolbarTest._checkFinish();
+        }
+      }
+      else {
+        try {
+          target(browser, tab);
+        }
+        catch (ex) {
+          ok(false, "" + ex);
+          DeveloperToolbarTest._finish();
+          throw ex;
+        }
+      }
+    });
+  }
+
+  browser.addEventListener("load", onTabLoad, true);
+};
+
+DeveloperToolbarTest._outstanding = [];
+
+DeveloperToolbarTest._checkFinish = function() {
+  if (DeveloperToolbarTest._outstanding.length == 0) {
+    DeveloperToolbarTest._finish();
+  }
+}
+
+DeveloperToolbarTest._finish = function() {
+  DeveloperToolbarTest.closeAllTabs();
+  finish();
+}
+
+DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
+  var todo = function() {
+    var reply = aFunc.apply(aScope, arguments);
+    DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
+      return aJob != todo;
+    });
+    DeveloperToolbarTest._checkFinish();
+    return reply;
+  }
+  DeveloperToolbarTest._outstanding.push(todo);
+  return todo;
+};
+
+DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
+  return function() {
+    ok(false, aMsg);
+    return aFunc.apply(aScope, arguments);
+  }
+};
+
+/**
+ *
+ */
+DeveloperToolbarTest.closeAllTabs = function() {
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
+};
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -8,25 +8,26 @@ const EXPORTED_SYMBOLS = [ "DeveloperToo
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
 
 const WEBCONSOLE_CONTENT_SCRIPT_URL =
   "chrome://browser/content/devtools/HUDService-content.js";
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/devtools/Commands.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "gcli",
                                   "resource:///modules/devtools/gcli.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "GcliCommands",
-                                  "resource:///modules/devtools/GcliCommands.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands",
+                                  "resource:///modules/devtools/CmdCmd.jsm");
 
 /**
  * A component to manage the global developer toolbar, which contains a GCLI
  * and buttons for various developer tools.
  * @param aChromeWindow The browser window to which this toolbar is attached
  * @param aToolbarElement See browser.xul:<toolbar id="developer-toolbar">
  */
 function DeveloperToolbar(aChromeWindow, aToolbarElement)
@@ -40,17 +41,17 @@ function DeveloperToolbar(aChromeWindow,
   this._lastState = NOTIFICATIONS.HIDE;
   this._pendingShowCallback = undefined;
   this._pendingHide = false;
   this._errorsCount = {};
   this._webConsoleButton = this._doc
                            .getElementById("developer-toolbar-webconsole");
 
   try {
-    GcliCommands.refreshAutoCommands(aChromeWindow);
+    CmdCommands.refreshAutoCommands(aChromeWindow);
   }
   catch (ex) {
     console.error(ex);
   }
 }
 
 /**
  * Inspector notifications dispatched through the nsIObserverService
@@ -551,32 +552,32 @@ function OutputPanel(aChromeDoc, aInput,
 
   /*
   <tooltip id="gcli-output"
          noautofocus="true"
          noautohide="true"
          class="gcli-panel">
     <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
                  id="gcli-output-frame"
-                 src="chrome://browser/content/devtools/gclioutput.xhtml"
+                 src="chrome://browser/content/devtools/commandlineoutput.xhtml"
                  flex="1"/>
   </tooltip>
   */
 
   // TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
   // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
   this._panel = aChromeDoc.createElement("tooltip");
 
   this._panel.id = "gcli-output";
   this._panel.classList.add("gcli-panel");
   this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
 
   this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
   this._frame.id = "gcli-output-frame";
-  this._frame.setAttribute("src", "chrome://browser/content/devtools/gclioutput.xhtml");
+  this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlineoutput.xhtml");
   this._frame.setAttribute("flex", "1");
   this._panel.appendChild(this._frame);
 
   this.displayedOutput = undefined;
 
   this._onload = this._onload.bind(this);
   this._frame.addEventListener("load", this._onload, true);
 
@@ -762,32 +763,32 @@ function TooltipPanel(aChromeDoc, aInput
   /*
   <tooltip id="gcli-tooltip"
          type="arrow"
          noautofocus="true"
          noautohide="true"
          class="gcli-panel">
     <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
                  id="gcli-tooltip-frame"
-                 src="chrome://browser/content/devtools/gclitooltip.xhtml"
+                 src="chrome://browser/content/devtools/commandlinetooltip.xhtml"
                  flex="1"/>
   </tooltip>
   */
 
   // TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
   // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
   this._panel = aChromeDoc.createElement("tooltip");
 
   this._panel.id = "gcli-tooltip";
   this._panel.classList.add("gcli-panel");
   this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
 
   this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
   this._frame.id = "gcli-tooltip-frame";
-  this._frame.setAttribute("src", "chrome://browser/content/devtools/gclitooltip.xhtml");
+  this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlinetooltip.xhtml");
   this._frame.setAttribute("flex", "1");
   this._panel.appendChild(this._frame);
 
   this._frame.addEventListener("load", this._onload, true);
 
   this.loaded = false;
   this.canHide = false;
 
--- a/browser/devtools/shared/test/Makefile.in
+++ b/browser/devtools/shared/test/Makefile.in
@@ -16,16 +16,18 @@ MOCHITEST_BROWSER_FILES = \
   browser_promise_basic.js \
   browser_require_basic.js \
   browser_templater_basic.js \
   browser_toolbar_basic.js \
   browser_toolbar_tooltip.js \
   browser_toolbar_webconsole_errors_count.js \
   browser_layoutHelpers.js \
   head.js \
+  helper.js \
+  leakhunt.js \
   $(NULL)
 
 MOCHITEST_BROWSER_FILES += \
   browser_templater_basic.html \
   browser_toolbar_basic.html \
   browser_toolbar_webconsole_errors_count.html \
   browser_layoutHelpers.html \
   browser_layoutHelpers_iframe.html \
--- a/browser/devtools/shared/test/head.js
+++ b/browser/devtools/shared/test/head.js
@@ -3,16 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 let console = (function() {
   let tempScope = {};
   Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
   return tempScope.console;
 })();
 
+// Import the GCLI test helper
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
+
 /**
  * Open a new tab at a URL and call a callback on load
  */
 function addTab(aURL, aCallback)
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
@@ -32,97 +36,16 @@ function addTab(aURL, aCallback)
 registerCleanupFunction(function tearDown() {
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 
   console = undefined;
 });
 
-/**
- * Various functions for testing DeveloperToolbar.
- * Parts of this code exist in:
- * - browser/devtools/commandline/test/head.js
- * - browser/devtools/shared/test/head.js
- */
-let DeveloperToolbarTest = {
-  /**
-   * Paranoid DeveloperToolbar.show();
-   */
-  show: function DTT_show(aCallback) {
-    if (DeveloperToolbar.visible) {
-      ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
-    }
-    else {
-      DeveloperToolbar.show(true, aCallback);
-    }
-  },
-
-  /**
-   * Paranoid DeveloperToolbar.hide();
-   */
-  hide: function DTT_hide() {
-    if (!DeveloperToolbar.visible) {
-      ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
-    }
-    else {
-      DeveloperToolbar.display.inputter.setInput("");
-      DeveloperToolbar.hide();
-    }
-  },
-
-  /**
-   * Quick wrapper around the things you need to do to run DeveloperToolbar
-   * command tests:
-   * - Set the pref 'devtools.toolbar.enabled' to true
-   * - Add a tab pointing at |uri|
-   * - Open the DeveloperToolbar
-   * - Register a cleanup function to undo the above
-   * - Run the tests
-   *
-   * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
-   * @param testFunc A function containing the tests to run. This should
-   * arrange for 'finish()' to be called on completion.
-   */
-  test: function DTT_test(uri, testFunc) {
-    let menuItem = document.getElementById("menu_devToolbar");
-    let command = document.getElementById("Tools:DevToolbar");
-    let appMenuItem = document.getElementById("appmenu_devToolbar");
-
-    registerCleanupFunction(function() {
-      DeveloperToolbarTest.hide();
-
-      // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
-      if (menuItem) menuItem.hidden = true;
-      if (command) command.setAttribute("disabled", "true");
-      if (appMenuItem) appMenuItem.hidden = true;
-    });
-
-    // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
-    if (menuItem) menuItem.hidden = false;
-    if (command) command.removeAttribute("disabled");
-    if (appMenuItem) appMenuItem.hidden = false;
-
-    addTab(uri, function(browser, tab) {
-      DeveloperToolbarTest.show(function() {
-
-        try {
-          testFunc(browser, tab);
-        }
-        catch (ex) {
-          ok(false, "" + ex);
-          console.error(ex);
-          finish();
-          throw ex;
-        }
-      });
-    });
-  },
-};
-
 function catchFail(func) {
   return function() {
     try {
       return func.apply(null, arguments);
     }
     catch (ex) {
       ok(false, ex);
       console.error(ex);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/test/helper.js
@@ -0,0 +1,459 @@
+/* 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/. */
+
+
+/*
+ *
+ *  DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
+ *  OF THIS FILE.
+ *
+ *  UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
+ *  THE NAUGHTY STEP.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *  FOR A LONG TIME.
+ *
+ */
+
+
+/**
+ * Various functions for testing DeveloperToolbar.
+ * Parts of this code exist in:
+ * - browser/devtools/commandline/test/head.js
+ * - browser/devtools/shared/test/head.js
+ */
+let DeveloperToolbarTest = { };
+
+/**
+ * Paranoid DeveloperToolbar.show();
+ */
+DeveloperToolbarTest.show = function DTT_show(aCallback) {
+  if (DeveloperToolbar.visible) {
+    ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.show(true, aCallback);
+  }
+};
+
+/**
+ * Paranoid DeveloperToolbar.hide();
+ */
+DeveloperToolbarTest.hide = function DTT_hide() {
+  if (!DeveloperToolbar.visible) {
+    ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.display.inputter.setInput("");
+    DeveloperToolbar.hide();
+  }
+};
+
+/**
+ * check() is the new status. Similar API except that it doesn't attempt to
+ * alter the display/requisition at all, and it makes extra checks.
+ * Test inputs
+ *   typed: The text to type at the input
+ * Available checks:
+ *   input: The text displayed in the input field
+ *   cursor: The position of the start of the cursor
+ *   status: One of "VALID", "ERROR", "INCOMPLETE"
+ *   emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
+ *   directTabText: Simple completion text
+ *   arrowTabText: When the completion is not an extension (without arrow)
+ *   markup: What state should the error markup be in. e.g. "VVVIIIEEE"
+ *   args: Maps of checks to make against the arguments:
+ *     value: i.e. assignment.value (which ignores defaultValue)
+ *     type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
+ *           Care should be taken with this since it's something of an
+ *           implementation detail
+ *     arg: The toString value of the argument
+ *     status: i.e. assignment.getStatus
+ *     message: i.e. assignment.getMessage
+ *     name: For commands - checks assignment.value.name
+ */
+DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
+  if (!checks.emptyParameters) {
+    checks.emptyParameters = [];
+  }
+  if (!checks.directTabText) {
+    checks.directTabText = '';
+  }
+  if (!checks.arrowTabText) {
+    checks.arrowTabText = '';
+  }
+
+  var display = DeveloperToolbar.display;
+
+  if (checks.typed) {
+    info('Starting tests for ' + checks.typed);
+    display.inputter.setInput(checks.typed);
+  }
+  else {
+    ok(false, "Missing typed for " + JSON.stringify(checks));
+    return;
+  }
+
+  if (checks.cursor) {
+    display.inputter.setCursor(checks.cursor)
+  }
+
+  var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
+
+  var requisition = display.requisition;
+  var completer = display.completer;
+  var actual = completer._getCompleterTemplateData();
+
+  /*
+  if (checks.input) {
+    is(display.inputter.element.value,
+            checks.input,
+            'input');
+  }
+
+  if (checks.cursor) {
+    is(display.inputter.element.selectionStart,
+            checks.cursor,
+            'cursor');
+  }
+  */
+
+  if (checks.status) {
+    is(requisition.getStatus().toString(),
+            checks.status,
+            'status');
+  }
+
+  if (checks.markup) {
+    var statusMarkup = requisition.getInputStatusMarkup(cursor);
+    var actualMarkup = statusMarkup.map(function(s) {
+      return Array(s.string.length + 1).join(s.status.toString()[0]);
+    }).join('');
+
+    is(checks.markup,
+            actualMarkup,
+            'markup');
+  }
+
+  if (checks.emptyParameters) {
+    var actualParams = actual.emptyParameters;
+    is(actualParams.length,
+            checks.emptyParameters.length,
+            'emptyParameters.length');
+
+    if (actualParams.length === checks.emptyParameters.length) {
+      for (var i = 0; i < actualParams.length; i++) {
+        is(actualParams[i].replace(/\u00a0/g, ' '),
+                checks.emptyParameters[i],
+                'emptyParameters[' + i + ']');
+      }
+    }
+  }
+
+  if (checks.directTabText) {
+    is(actual.directTabText,
+            checks.directTabText,
+            'directTabText');
+  }
+
+  if (checks.arrowTabText) {
+    is(actual.arrowTabText,
+            ' \u00a0\u21E5 ' + checks.arrowTabText,
+            'arrowTabText');
+  }
+
+  if (checks.args) {
+    Object.keys(checks.args).forEach(function(paramName) {
+      var check = checks.args[paramName];
+
+      var assignment;
+      if (paramName === 'command') {
+        assignment = requisition.commandAssignment;
+      }
+      else {
+        assignment = requisition.getAssignment(paramName);
+      }
+
+      if (assignment == null) {
+        ok(false, 'Unknown parameter: ' + paramName);
+        return;
+      }
+
+      if (check.value) {
+        is(assignment.value,
+                check.value,
+                'checkStatus value for ' + paramName);
+      }
+
+      if (check.name) {
+        is(assignment.value.name,
+                check.name,
+                'checkStatus name for ' + paramName);
+      }
+
+      if (check.type) {
+        is(assignment.arg.type,
+                check.type,
+                'checkStatus type for ' + paramName);
+      }
+
+      if (check.arg) {
+        is(assignment.arg.toString(),
+                check.arg,
+                'checkStatus arg for ' + paramName);
+      }
+
+      if (check.status) {
+        is(assignment.getStatus().toString(),
+                check.status,
+                'checkStatus status for ' + paramName);
+      }
+
+      if (check.message) {
+        is(assignment.getMessage(),
+                check.message,
+                'checkStatus message for ' + paramName);
+      }
+    });
+  }
+};
+
+/**
+ * Execute a command:
+ *
+ * DeveloperToolbarTest.exec({
+ *   // Test inputs
+ *   typed: "echo hi",        // Optional, uses existing if undefined
+ *
+ *   // Thing to check
+ *   args: { message: "hi" }, // Check that the args were understood properly
+ *   outputMatch: /^hi$/,     // RegExp to test against textContent of output
+ *                            // (can also be array of RegExps)
+ *   blankOutput: true,       // Special checks when there is no output
+ * });
+ */
+DeveloperToolbarTest.exec = function DTT_exec(tests) {
+  tests = tests || {};
+
+  if (tests.typed) {
+    DeveloperToolbar.display.inputter.setInput(tests.typed);
+  }
+
+  let typed = DeveloperToolbar.display.inputter.getInputState().typed;
+  let output = DeveloperToolbar.display.requisition.exec();
+
+  is(typed, output.typed, 'output.command for: ' + typed);
+
+  if (tests.completed !== false) {
+    ok(output.completed, 'output.completed false for: ' + typed);
+  }
+  else {
+    // It is actually an error if we say something is async and it turns
+    // out not to be? For now we're saying 'no'
+    // ok(!output.completed, 'output.completed true for: ' + typed);
+  }
+
+  if (tests.args != null) {
+    is(Object.keys(tests.args).length, Object.keys(output.args).length,
+       'arg count for ' + typed);
+
+    Object.keys(output.args).forEach(function(arg) {
+      let expectedArg = tests.args[arg];
+      let actualArg = output.args[arg];
+
+      if (typeof expectedArg === 'function') {
+        ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
+      }
+      else {
+        if (Array.isArray(expectedArg)) {
+          if (!Array.isArray(actualArg)) {
+            ok(false, 'actual is not an array. ' + typed + '/' + arg);
+            return;
+          }
+
+          is(expectedArg.length, actualArg.length,
+                  'array length: ' + typed + '/' + arg);
+          for (let i = 0; i < expectedArg.length; i++) {
+            is(expectedArg[i], actualArg[i],
+                    'member: "' + typed + '/' + arg + '/' + i);
+          }
+        }
+        else {
+          is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
+        }
+      }
+    });
+  }
+
+  let displayed = DeveloperToolbar.outputPanel._div.textContent;
+
+  if (tests.outputMatch) {
+    var doTest = function(match, against) {
+      if (!match.test(against)) {
+        ok(false, "html output for " + typed + " against " + match.source +
+                " (textContent sent to info)");
+        info("Actual textContent");
+        info(against);
+      }
+    }
+    if (Array.isArray(tests.outputMatch)) {
+      tests.outputMatch.forEach(function(match) {
+        doTest(match, displayed);
+      });
+    }
+    else {
+      doTest(tests.outputMatch, displayed);
+    }
+  }
+
+  if (tests.blankOutput != null) {
+    if (!/^$/.test(displayed)) {
+      ok(false, "html output for " + typed + " (textContent sent to info)");
+      info("Actual textContent");
+      info(displayed);
+    }
+  }
+};
+
+/**
+ * Quick wrapper around the things you need to do to run DeveloperToolbar
+ * command tests:
+ * - Set the pref 'devtools.toolbar.enabled' to true
+ * - Add a tab pointing at |uri|
+ * - Open the DeveloperToolbar
+ * - Register a cleanup function to undo the above
+ * - Run the tests
+ *
+ * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
+ * @param target Either a function or array of functions containing the tests
+ * to run. If an array of test function is passed then we will clear up after
+ * the tests have completed. If a single test function is passed then this
+ * function should arrange for 'finish()' to be called on completion.
+ */
+DeveloperToolbarTest.test = function DTT_test(uri, target) {
+  let menuItem = document.getElementById("menu_devToolbar");
+  let command = document.getElementById("Tools:DevToolbar");
+  let appMenuItem = document.getElementById("appmenu_devToolbar");
+
+  registerCleanupFunction(function() {
+    DeveloperToolbarTest.hide();
+
+    // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
+    if (menuItem) {
+      menuItem.hidden = true;
+    }
+    if (command) {
+      command.setAttribute("disabled", "true");
+    }
+    if (appMenuItem) {
+      appMenuItem.hidden = true;
+    }
+
+    // leakHunt({ DeveloperToolbar: DeveloperToolbar });
+  });
+
+  // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
+  if (menuItem) {
+    menuItem.hidden = false;
+  }
+  if (command) {
+    command.removeAttribute("disabled");
+  }
+  if (appMenuItem) {
+    appMenuItem.hidden = false;
+  }
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  content.location = uri;
+
+  let tab = gBrowser.selectedTab;
+  let browser = gBrowser.getBrowserForTab(tab);
+
+  var onTabLoad = function() {
+    browser.removeEventListener("load", onTabLoad, true);
+
+    DeveloperToolbarTest.show(function() {
+      if (Array.isArray(target)) {
+        try {
+          target.forEach(function(func) {
+            func(browser, tab);
+          })
+        }
+        finally {
+          DeveloperToolbarTest._checkFinish();
+        }
+      }
+      else {
+        try {
+          target(browser, tab);
+        }
+        catch (ex) {
+          ok(false, "" + ex);
+          DeveloperToolbarTest._finish();
+          throw ex;
+        }
+      }
+    });
+  }
+
+  browser.addEventListener("load", onTabLoad, true);
+};
+
+DeveloperToolbarTest._outstanding = [];
+
+DeveloperToolbarTest._checkFinish = function() {
+  if (DeveloperToolbarTest._outstanding.length == 0) {
+    DeveloperToolbarTest._finish();
+  }
+}
+
+DeveloperToolbarTest._finish = function() {
+  DeveloperToolbarTest.closeAllTabs();
+  finish();
+}
+
+DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
+  var todo = function() {
+    var reply = aFunc.apply(aScope, arguments);
+    DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
+      return aJob != todo;
+    });
+    DeveloperToolbarTest._checkFinish();
+    return reply;
+  }
+  DeveloperToolbarTest._outstanding.push(todo);
+  return todo;
+};
+
+DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
+  return function() {
+    ok(false, aMsg);
+    return aFunc.apply(aScope, arguments);
+  }
+};
+
+/**
+ *
+ */
+DeveloperToolbarTest.closeAllTabs = function() {
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/test/leakhunt.js
@@ -0,0 +1,157 @@
+/* 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/. */
+
+/**
+ * Memory leak hunter. Walks a tree of objects looking for DOM nodes.
+ * Usage:
+ * leakHunt({
+ *   thing: thing,
+ *   otherthing: otherthing
+ * });
+ */
+
+var noRecurse = [
+  /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/,
+  /^Window$/, /^Document$/,
+  /^XULDocument$/, /^XULElement$/,
+  /^DOMWindow$/, /^HTMLDocument$/, /^HTML.*Element$/
+];
+
+var hide = [ /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/ ];
+
+function leakHunt(root, path, seen) {
+  path = path || [];
+  seen = seen || [];
+
+  try {
+    var output = leakHuntInner(root, path, seen);
+    output.forEach(function(line) {
+      dump(line + '\n');
+    });
+  }
+  catch (ex) {
+    dump(ex + '\n');
+  }
+}
+
+function leakHuntInner(root, path, seen) {
+  var prefix = new Array(path.length).join('  ');
+
+  var reply = [];
+  function log(msg) {
+    reply.push(msg);
+  }
+
+  var direct
+  try {
+    direct = Object.keys(root);
+  }
+  catch (ex) {
+    log(prefix + '  Error enumerating: ' + ex);
+    return reply;
+  }
+
+  for (var prop in root) {
+    var newPath = path.slice();
+    newPath.push(prop);
+    prefix = new Array(newPath.length).join('  ');
+
+    var data;
+    try {
+      data = root[prop];
+    }
+    catch (ex) {
+      log(prefix + prop + '  Error reading: ' + ex);
+      continue;
+    }
+
+    var recurse = true;
+    var message = getType(data);
+
+    if (matchesAnyPattern(message, hide)) {
+      continue;
+    }
+
+    if (message === 'function' && direct.indexOf(prop) == -1) {
+      continue;
+    }
+
+    if (message === 'string') {
+      var extra = data.length > 10 ? data.substring(0, 9) + '_' : data;
+      message += ' "' + extra.replace(/\n/g, "|") + '"';
+      recurse = false;
+    }
+    else if (matchesAnyPattern(message, noRecurse)) {
+      message += ' (no recurse)'
+      recurse = false;
+    }
+    else if (seen.indexOf(data) !== -1) {
+      message += ' (already seen)';
+      recurse = false;
+    }
+
+    if (recurse) {
+      seen.push(data);
+      var lines = leakHuntInner(data, newPath, seen);
+      if (lines.length == 0) {
+        if (message !== 'function') {
+          log(prefix + prop + ' = ' + message + ' { }');
+        }
+      }
+      else {
+        log(prefix + prop + ' = ' + message + ' {');
+        lines.forEach(function(line) {
+          reply.push(line);
+        });
+        log(prefix + '}');
+      }
+    }
+    else {
+      log(prefix + prop + ' = ' + message);
+    }
+  }
+
+  return reply;
+}
+
+function matchesAnyPattern(str, patterns) {
+  var match = false;
+  patterns.forEach(function(pattern) {
+    if (str.match(pattern)) {
+      match = true;
+    }
+  });
+  return match;
+}
+
+function getType(data) {
+  if (data === null) {
+    return 'null';
+  }
+  if (data === undefined) {
+    return 'undefined';
+  }
+
+  var type = typeof data;
+  if (type === 'object' || type === 'Object') {
+    type = getCtorName(data);
+  }
+
+  return type;
+}
+
+function getCtorName(aObj) {
+  try {
+    if (aObj.constructor && aObj.constructor.name) {
+      return aObj.constructor.name;
+    }
+  }
+  catch (ex) {
+    return 'UnknownObject';
+  }
+
+  // If that fails, use Objects toString which sometimes gives something
+  // better than 'Object', and at least defaults to Object if nothing better
+  return Object.prototype.toString.call(aObj).slice(8, -1);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/CmdEdit.jsm
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let EXPORTED_SYMBOLS = [ ];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/devtools/gcli.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
+                                  "resource:///modules/HUDService.jsm");
+
+/**
+ * 'edit' command
+ */
+gcli.addCommand({
+  name: "edit",
+  description: gcli.lookup("editDesc"),
+  manual: gcli.lookup("editManual2"),
+  params: [
+     {
+       name: 'resource',
+       type: {
+         name: 'resource',
+         include: 'text/css'
+       },
+       description: gcli.lookup("editResourceDesc")
+     },
+     {
+       name: "line",
+       defaultValue: 1,
+       type: {
+         name: "number",
+         min: 1,
+         step: 10
+       },
+       description: gcli.lookup("editLineToJumpToDesc")
+     }
+   ],
+   exec: function(args, context) {
+     let win = HUDService.currentContext();
+     win.StyleEditor.openChrome(args.resource.element, args.line);
+   }
+});
--- a/browser/devtools/styleeditor/test/Makefile.in
+++ b/browser/devtools/styleeditor/test/Makefile.in
@@ -9,31 +9,37 @@ VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
                  browser_styleeditor_enabled.js \
                  browser_styleeditor_filesave.js \
+                 browser_styleeditor_cmd_edit.js \
+                 browser_styleeditor_cmd_edit.html \
                  browser_styleeditor_import.js \
                  browser_styleeditor_init.js \
                  browser_styleeditor_loading.js \
                  browser_styleeditor_new.js \
                  browser_styleeditor_passedinsheet.js \
                  browser_styleeditor_pretty.js \
                  browser_styleeditor_readonly.js \
                  browser_styleeditor_reopen.js \
                  browser_styleeditor_sv_keynav.js \
                  browser_styleeditor_sv_resize.js \
                  four.html \
                  head.js \
+                 helper.js \
                  media.html \
                  media-small.css \
                  minified.html \
+                 resources_inpage.jsi \
+                 resources_inpage1.css \
+                 resources_inpage2.css \
                  simple.css \
                  simple.css.gz \
                  simple.css.gz^headers^ \
                  simple.gz.html \
                  simple.html \
                  $(NULL)
 
 libs::	$(_BROWSER_TEST_FILES)
rename from browser/devtools/commandline/test/resources.html
rename to browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.html
--- a/browser/devtools/commandline/test/resources.html
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.html
@@ -11,17 +11,17 @@
       var div = document.createElement('div');
       div.id = 'divid';
       div.classList.add('divclass');
       div.appendChild(document.createTextNode('div'));
       div.setAttribute('data-a1', 'div');
       pid.parentNode.appendChild(div);
     });
   </script>
-  <script src="resources_inpage.js"></script>
+  <script src="resources_inpage.jsi"></script>
   <link rel="stylesheet" type="text/css" href="resources_inpage1.css"/>
   <link rel="stylesheet" type="text/css" href="resources_inpage2.css"/>
   <style type="text/css">
     p { color: #800; }
     div { color: #008; }
     h4 { color: #080; }
     h3 { color: #880; }
   </style>
rename from browser/devtools/commandline/test/browser_gcli_edit.js
rename to browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.js
--- a/browser/devtools/commandline/test/browser_gcli_edit.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.js
@@ -1,22 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the edit command works
 
-const TEST_URI = TEST_BASE_HTTP + "resources.html";
+const TEST_URI = "http://example.com/browser/browser/devtools/styleeditor/" +
+                 "test/browser_styleeditor_cmd_edit.html";
 
 function test() {
-  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
-    testEditStatus(browser, tab);
-    // Bug 759853
-    // testEditExec(browser, tab); // calls finish()
-    finish();
-  });
+  DeveloperToolbarTest.test(TEST_URI, [ testEditStatus ]);
+  // Bug 759853
+  // testEditExec
 }
 
 function testEditStatus(browser, tab) {
   DeveloperToolbarTest.checkInputStatus({
     typed:  "edit",
     markup: "VVVV",
     status: "ERROR",
     emptyParameters: [ " <resource>", " [line]" ],
@@ -37,36 +35,36 @@ function testEditStatus(browser, tab) {
     directTabText: "ss#style2",
     emptyParameters: [ " [line]" ],
   });
 
   DeveloperToolbarTest.checkInputStatus({
     typed:  "edit http",
     markup: "VVVVVIIII",
     status: "ERROR",
-    directTabText: "://example.com/browser/browser/devtools/commandline/test/resources_inpage1.css",
+    directTabText: "://example.com/browser/browser/devtools/styleeditor/test/resources_inpage1.css",
     arrowTabText: "",
     emptyParameters: [ " [line]" ],
   });
 
   DeveloperToolbarTest.checkInputStatus({
     typed:  "edit page1",
     markup: "VVVVVIIIII",
     status: "ERROR",
     directTabText: "",
-    arrowTabText: "http://example.com/browser/browser/devtools/commandline/test/resources_inpage1.css",
+    arrowTabText: "http://example.com/browser/browser/devtools/styleeditor/test/resources_inpage1.css",
     emptyParameters: [ " [line]" ],
   });
 
   DeveloperToolbarTest.checkInputStatus({
     typed:  "edit page2",
     markup: "VVVVVIIIII",
     status: "ERROR",
     directTabText: "",
-    arrowTabText: "http://example.com/browser/browser/devtools/commandline/test/resources_inpage2.css",
+    arrowTabText: "http://example.com/browser/browser/devtools/styleeditor/test/resources_inpage2.css",
     emptyParameters: [ " [line]" ],
   });
 
   DeveloperToolbarTest.checkInputStatus({
     typed:  "edit stylez",
     markup: "VVVVVEEEEEE",
     status: "ERROR",
     directTabText: "",
--- a/browser/devtools/styleeditor/test/head.js
+++ b/browser/devtools/styleeditor/test/head.js
@@ -1,18 +1,21 @@
-/* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/styleeditor/test/";
 const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/styleeditor/test/";
 const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleeditor/test/";
 
 let gChromeWindow;               //StyleEditorChrome window
 
+// Import the GCLI test helper
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
+
 function cleanup()
 {
   if (gChromeWindow) {
     gChromeWindow.close();
     gChromeWindow = null;
   }
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/helper.js
@@ -0,0 +1,459 @@
+/* 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/. */
+
+
+/*
+ *
+ *  DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES
+ *  OF THIS FILE.
+ *
+ *  UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON
+ *  THE NAUGHTY STEP.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *  FOR A LONG TIME.
+ *
+ */
+
+
+/**
+ * Various functions for testing DeveloperToolbar.
+ * Parts of this code exist in:
+ * - browser/devtools/commandline/test/head.js
+ * - browser/devtools/shared/test/head.js
+ */
+let DeveloperToolbarTest = { };
+
+/**
+ * Paranoid DeveloperToolbar.show();
+ */
+DeveloperToolbarTest.show = function DTT_show(aCallback) {
+  if (DeveloperToolbar.visible) {
+    ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.show(true, aCallback);
+  }
+};
+
+/**
+ * Paranoid DeveloperToolbar.hide();
+ */
+DeveloperToolbarTest.hide = function DTT_hide() {
+  if (!DeveloperToolbar.visible) {
+    ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
+  }
+  else {
+    DeveloperToolbar.display.inputter.setInput("");
+    DeveloperToolbar.hide();
+  }
+};
+
+/**
+ * check() is the new status. Similar API except that it doesn't attempt to
+ * alter the display/requisition at all, and it makes extra checks.
+ * Test inputs
+ *   typed: The text to type at the input
+ * Available checks:
+ *   input: The text displayed in the input field
+ *   cursor: The position of the start of the cursor
+ *   status: One of "VALID", "ERROR", "INCOMPLETE"
+ *   emptyParameters: Array of parameters still to type. e.g. [ "<message>" ]
+ *   directTabText: Simple completion text
+ *   arrowTabText: When the completion is not an extension (without arrow)
+ *   markup: What state should the error markup be in. e.g. "VVVIIIEEE"
+ *   args: Maps of checks to make against the arguments:
+ *     value: i.e. assignment.value (which ignores defaultValue)
+ *     type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
+ *           Care should be taken with this since it's something of an
+ *           implementation detail
+ *     arg: The toString value of the argument
+ *     status: i.e. assignment.getStatus
+ *     message: i.e. assignment.getMessage
+ *     name: For commands - checks assignment.value.name
+ */
+DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
+  if (!checks.emptyParameters) {
+    checks.emptyParameters = [];
+  }
+  if (!checks.directTabText) {
+    checks.directTabText = '';
+  }
+  if (!checks.arrowTabText) {
+    checks.arrowTabText = '';
+  }
+
+  var display = DeveloperToolbar.display;
+
+  if (checks.typed) {
+    info('Starting tests for ' + checks.typed);
+    display.inputter.setInput(checks.typed);
+  }
+  else {
+    ok(false, "Missing typed for " + JSON.stringify(checks));
+    return;
+  }
+
+  if (checks.cursor) {
+    display.inputter.setCursor(checks.cursor)
+  }
+
+  var cursor = checks.cursor ? checks.cursor.start : checks.typed.length;
+
+  var requisition = display.requisition;
+  var completer = display.completer;
+  var actual = completer._getCompleterTemplateData();
+
+  /*
+  if (checks.input) {
+    is(display.inputter.element.value,
+            checks.input,
+            'input');
+  }
+
+  if (checks.cursor) {
+    is(display.inputter.element.selectionStart,
+            checks.cursor,
+            'cursor');
+  }
+  */
+
+  if (checks.status) {
+    is(requisition.getStatus().toString(),
+            checks.status,
+            'status');
+  }
+
+  if (checks.markup) {
+    var statusMarkup = requisition.getInputStatusMarkup(cursor);
+    var actualMarkup = statusMarkup.map(function(s) {
+      return Array(s.string.length + 1).join(s.status.toString()[0]);
+    }).join('');
+
+    is(checks.markup,
+            actualMarkup,
+            'markup');
+  }
+
+  if (checks.emptyParameters) {
+    var actualParams = actual.emptyParameters;
+    is(actualParams.length,
+            checks.emptyParameters.length,
+            'emptyParameters.length');
+
+    if (actualParams.length === checks.emptyParameters.length) {
+      for (var i = 0; i < actualParams.length; i++) {
+        is(actualParams[i].replace(/\u00a0/g, ' '),
+                checks.emptyParameters[i],
+                'emptyParameters[' + i + ']');
+      }
+    }
+  }
+
+  if (checks.directTabText) {
+    is(actual.directTabText,
+            checks.directTabText,
+            'directTabText');
+  }
+
+  if (checks.arrowTabText) {
+    is(actual.arrowTabText,
+            ' \u00a0\u21E5 ' + checks.arrowTabText,
+            'arrowTabText');
+  }
+
+  if (checks.args) {
+    Object.keys(checks.args).forEach(function(paramName) {
+      var check = checks.args[paramName];
+
+      var assignment;
+      if (paramName === 'command') {
+        assignment = requisition.commandAssignment;
+      }
+      else {
+        assignment = requisition.getAssignment(paramName);
+      }
+
+      if (assignment == null) {
+        ok(false, 'Unknown parameter: ' + paramName);
+        return;
+      }
+
+      if (check.value) {
+        is(assignment.value,
+                check.value,
+                'checkStatus value for ' + paramName);
+      }
+
+      if (check.name) {
+        is(assignment.value.name,
+                check.name,
+                'checkStatus name for ' + paramName);
+      }
+
+      if (check.type) {
+        is(assignment.arg.type,
+                check.type,
+                'checkStatus type for ' + paramName);
+      }
+
+      if (check.arg) {
+        is(assignment.arg.toString(),
+                check.arg,
+                'checkStatus arg for ' + paramName);
+      }
+
+      if (check.status) {
+        is(assignment.getStatus().toString(),
+                check.status,
+                'checkStatus status for ' + paramName);
+      }
+
+      if (check.message) {
+        is(assignment.getMessage(),
+                check.message,
+                'checkStatus message for ' + paramName);
+      }
+    });
+  }
+};
+
+/**
+ * Execute a command:
+ *
+ * DeveloperToolbarTest.exec({
+ *   // Test inputs
+ *   typed: "echo hi",        // Optional, uses existing if undefined
+ *
+ *   // Thing to check
+ *   args: { message: "hi" }, // Check that the args were understood properly
+ *   outputMatch: /^hi$/,     // RegExp to test against textContent of output
+ *                            // (can also be array of RegExps)
+ *   blankOutput: true,       // Special checks when there is no output
+ * });
+ */
+DeveloperToolbarTest.exec = function DTT_exec(tests) {
+  tests = tests || {};
+
+  if (tests.typed) {
+    DeveloperToolbar.display.inputter.setInput(tests.typed);
+  }
+
+  let typed = DeveloperToolbar.display.inputter.getInputState().typed;
+  let output = DeveloperToolbar.display.requisition.exec();
+
+  is(typed, output.typed, 'output.command for: ' + typed);
+
+  if (tests.completed !== false) {
+    ok(output.completed, 'output.completed false for: ' + typed);
+  }
+  else {
+    // It is actually an error if we say something is async and it turns
+    // out not to be? For now we're saying 'no'
+    // ok(!output.completed, 'output.completed true for: ' + typed);
+  }
+
+  if (tests.args != null) {
+    is(Object.keys(tests.args).length, Object.keys(output.args).length,
+       'arg count for ' + typed);
+
+    Object.keys(output.args).forEach(function(arg) {
+      let expectedArg = tests.args[arg];
+      let actualArg = output.args[arg];
+
+      if (typeof expectedArg === 'function') {
+        ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg);
+      }
+      else {
+        if (Array.isArray(expectedArg)) {
+          if (!Array.isArray(actualArg)) {
+            ok(false, 'actual is not an array. ' + typed + '/' + arg);
+            return;
+          }
+
+          is(expectedArg.length, actualArg.length,
+                  'array length: ' + typed + '/' + arg);
+          for (let i = 0; i < expectedArg.length; i++) {
+            is(expectedArg[i], actualArg[i],
+                    'member: "' + typed + '/' + arg + '/' + i);
+          }
+        }
+        else {
+          is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
+        }
+      }
+    });
+  }
+
+  let displayed = DeveloperToolbar.outputPanel._div.textContent;
+
+  if (tests.outputMatch) {
+    var doTest = function(match, against) {
+      if (!match.test(against)) {
+        ok(false, "html output for " + typed + " against " + match.source +
+                " (textContent sent to info)");
+        info("Actual textContent");
+        info(against);
+      }
+    }
+    if (Array.isArray(tests.outputMatch)) {
+      tests.outputMatch.forEach(function(match) {
+        doTest(match, displayed);
+      });
+    }
+    else {
+      doTest(tests.outputMatch, displayed);
+    }
+  }
+
+  if (tests.blankOutput != null) {
+    if (!/^$/.test(displayed)) {
+      ok(false, "html output for " + typed + " (textContent sent to info)");
+      info("Actual textContent");
+      info(displayed);
+    }
+  }
+};
+
+/**
+ * Quick wrapper around the things you need to do to run DeveloperToolbar
+ * command tests:
+ * - Set the pref 'devtools.toolbar.enabled' to true
+ * - Add a tab pointing at |uri|
+ * - Open the DeveloperToolbar
+ * - Register a cleanup function to undo the above
+ * - Run the tests
+ *
+ * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
+ * @param target Either a function or array of functions containing the tests
+ * to run. If an array of test function is passed then we will clear up after
+ * the tests have completed. If a single test function is passed then this
+ * function should arrange for 'finish()' to be called on completion.
+ */
+DeveloperToolbarTest.test = function DTT_test(uri, target) {
+  let menuItem = document.getElementById("menu_devToolbar");
+  let command = document.getElementById("Tools:DevToolbar");
+  let appMenuItem = document.getElementById("appmenu_devToolbar");
+
+  registerCleanupFunction(function() {
+    DeveloperToolbarTest.hide();
+
+    // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
+    if (menuItem) {
+      menuItem.hidden = true;
+    }
+    if (command) {
+      command.setAttribute("disabled", "true");
+    }
+    if (appMenuItem) {
+      appMenuItem.hidden = true;
+    }
+
+    // leakHunt({ DeveloperToolbar: DeveloperToolbar });
+  });
+
+  // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
+  if (menuItem) {
+    menuItem.hidden = false;
+  }
+  if (command) {
+    command.removeAttribute("disabled");
+  }
+  if (appMenuItem) {
+    appMenuItem.hidden = false;
+  }
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  content.location = uri;
+
+  let tab = gBrowser.selectedTab;
+  let browser = gBrowser.getBrowserForTab(tab);
+
+  var onTabLoad = function() {
+    browser.removeEventListener("load", onTabLoad, true);
+
+    DeveloperToolbarTest.show(function() {
+      if (Array.isArray(target)) {
+        try {
+          target.forEach(function(func) {
+            func(browser, tab);
+          })
+        }
+        finally {
+          DeveloperToolbarTest._checkFinish();
+        }
+      }
+      else {
+        try {
+          target(browser, tab);
+        }
+        catch (ex) {
+          ok(false, "" + ex);
+          DeveloperToolbarTest._finish();
+          throw ex;
+        }
+      }
+    });
+  }
+
+  browser.addEventListener("load", onTabLoad, true);
+};
+
+DeveloperToolbarTest._outstanding = [];
+
+DeveloperToolbarTest._checkFinish = function() {
+  if (DeveloperToolbarTest._outstanding.length == 0) {
+    DeveloperToolbarTest._finish();
+  }
+}
+
+DeveloperToolbarTest._finish = function() {
+  DeveloperToolbarTest.closeAllTabs();
+  finish();
+}
+
+DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
+  var todo = function() {
+    var reply = aFunc.apply(aScope, arguments);
+    DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
+      return aJob != todo;
+    });
+    DeveloperToolbarTest._checkFinish();
+    return reply;
+  }
+  DeveloperToolbarTest._outstanding.push(todo);
+  return todo;
+};
+
+DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
+  return function() {
+    ok(false, aMsg);
+    return aFunc.apply(aScope, arguments);
+  }
+};
+
+/**
+ *
+ */
+DeveloperToolbarTest.closeAllTabs = function() {
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
+};
rename from browser/devtools/commandline/test/resources_inpage.js
rename to browser/devtools/styleeditor/test/resources_inpage.jsi
--- a/browser/devtools/commandline/test/resources_inpage.js
+++ b/browser/devtools/styleeditor/test/resources_inpage.jsi
@@ -1,10 +1,10 @@
 
-// This script is used from within browser_gcli_edit.html
+// This script is used from within browser_styleeditor_cmd_edit.html
 
 window.addEventListener('load', function() {
   var pid = document.getElementById('pid');
   var h3 = document.createElement('h3');
   h3.id = 'h3id';
   h3.classList.add('h3class');
   h3.appendChild(document.createTextNode('h3'));
   h3.setAttribute('data-a1', 'h3');
rename from browser/devtools/commandline/test/resources_inpage1.css
rename to browser/devtools/styleeditor/test/resources_inpage1.css
rename from browser/devtools/commandline/test/resources_inpage2.css
rename to browser/devtools/styleeditor/test/resources_inpage2.css
rename from browser/devtools/commandline/GcliTiltCommands.jsm
rename to browser/devtools/tilt/CmdTilt.jsm
rename from browser/themes/gnomestripe/devtools/gcli.css
rename to browser/themes/gnomestripe/devtools/commandline.css
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -102,17 +102,17 @@ browser.jar:
   skin/classic/browser/devtools/arrows.png            (devtools/arrows.png)
   skin/classic/browser/devtools/commandline.png       (devtools/commandline.png)
   skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
   skin/classic/browser/devtools/goto-mdn.png          (devtools/goto-mdn.png)
   skin/classic/browser/devtools/csshtmltree.css       (devtools/csshtmltree.css)
   skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
   skin/classic/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
   skin/classic/browser/devtools/webconsole.png                  (devtools/webconsole.png)
-  skin/classic/browser/devtools/gcli.css              (devtools/gcli.css)
+  skin/classic/browser/devtools/commandline.css              (devtools/commandline.css)
   skin/classic/browser/devtools/htmlpanel.css         (devtools/htmlpanel.css)
   skin/classic/browser/devtools/markup-view.css      (devtools/markup-view.css)
   skin/classic/browser/devtools/orion.css             (devtools/orion.css)
   skin/classic/browser/devtools/orion-container.css   (devtools/orion-container.css)
   skin/classic/browser/devtools/orion-task.png        (devtools/orion-task.png)
   skin/classic/browser/devtools/orion-breakpoint.png  (devtools/orion-breakpoint.png)
   skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton.png                 (devtools/breadcrumbs-scrollbutton.png)
rename from browser/themes/pinstripe/devtools/gcli.css
rename to browser/themes/pinstripe/devtools/commandline.css
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -138,17 +138,17 @@ browser.jar:
   skin/classic/browser/tabview/tabview.png                  (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css                  (tabview/tabview.css)
 * skin/classic/browser/devtools/common.css                  (devtools/common.css)
   skin/classic/browser/devtools/arrows.png                  (devtools/arrows.png)
   skin/classic/browser/devtools/commandline.png             (devtools/commandline.png)
   skin/classic/browser/devtools/alerticon-warning.png       (devtools/alerticon-warning.png)
   skin/classic/browser/devtools/goto-mdn.png                (devtools/goto-mdn.png)
   skin/classic/browser/devtools/csshtmltree.css             (devtools/csshtmltree.css)
-  skin/classic/browser/devtools/gcli.css                    (devtools/gcli.css)
+  skin/classic/browser/devtools/commandline.css                    (devtools/commandline.css)
   skin/classic/browser/devtools/htmlpanel.css               (devtools/htmlpanel.css)
   skin/classic/browser/devtools/markup-view.css             (devtools/markup-view.css)
   skin/classic/browser/devtools/orion.css                   (devtools/orion.css)
   skin/classic/browser/devtools/orion-container.css         (devtools/orion-container.css)
   skin/classic/browser/devtools/orion-task.png              (devtools/orion-task.png)
   skin/classic/browser/devtools/orion-breakpoint.png        (devtools/orion-breakpoint.png)
   skin/classic/browser/devtools/orion-debug-location.png    (devtools/orion-debug-location.png)
   skin/classic/browser/devtools/toolbarbutton-close.png     (devtools/toolbarbutton-close.png)
rename from browser/themes/winstripe/devtools/gcli.css
rename to browser/themes/winstripe/devtools/commandline.css
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -126,17 +126,17 @@ browser.jar:
         skin/classic/browser/tabview/tabview-inverted.png           (tabview/tabview-inverted.png)
         skin/classic/browser/tabview/tabview.css                    (tabview/tabview.css)
         skin/classic/browser/devtools/common.css                    (devtools/common.css)
         skin/classic/browser/devtools/arrows.png                    (devtools/arrows.png)
         skin/classic/browser/devtools/commandline.png               (devtools/commandline.png)
         skin/classic/browser/devtools/alerticon-warning.png         (devtools/alerticon-warning.png)
         skin/classic/browser/devtools/goto-mdn.png                  (devtools/goto-mdn.png)
         skin/classic/browser/devtools/csshtmltree.css               (devtools/csshtmltree.css)
-        skin/classic/browser/devtools/gcli.css                      (devtools/gcli.css)
+        skin/classic/browser/devtools/commandline.css                      (devtools/commandline.css)
         skin/classic/browser/devtools/htmlpanel.css                 (devtools/htmlpanel.css)
         skin/classic/browser/devtools/markup-view.css               (devtools/markup-view.css)
         skin/classic/browser/devtools/orion.css                     (devtools/orion.css)
         skin/classic/browser/devtools/orion-container.css           (devtools/orion-container.css)
         skin/classic/browser/devtools/orion-task.png                (devtools/orion-task.png)
         skin/classic/browser/devtools/orion-breakpoint.png          (devtools/orion-breakpoint.png)
         skin/classic/browser/devtools/orion-debug-location.png      (devtools/orion-debug-location.png)
         skin/classic/browser/devtools/toolbarbutton-close.png       (devtools/toolbarbutton-close.png)
@@ -329,17 +329,17 @@ browser.jar:
         skin/classic/aero/browser/tabview/tabview-inverted.png       (tabview/tabview-inverted.png)
         skin/classic/aero/browser/tabview/tabview.css                (tabview/tabview.css)
         skin/classic/aero/browser/devtools/common.css                (devtools/common.css)
         skin/classic/aero/browser/devtools/arrows.png                (devtools/arrows.png)
         skin/classic/aero/browser/devtools/commandline.png           (devtools/commandline.png)
         skin/classic/aero/browser/devtools/alerticon-warning.png     (devtools/alerticon-warning.png)
         skin/classic/aero/browser/devtools/goto-mdn.png              (devtools/goto-mdn.png)
         skin/classic/aero/browser/devtools/csshtmltree.css           (devtools/csshtmltree.css)
-        skin/classic/aero/browser/devtools/gcli.css                  (devtools/gcli.css)
+        skin/classic/aero/browser/devtools/commandline.css                  (devtools/commandline.css)
         skin/classic/aero/browser/devtools/htmlpanel.css             (devtools/htmlpanel.css)
         skin/classic/aero/browser/devtools/markup-view.css           (devtools/markup-view.css)
         skin/classic/aero/browser/devtools/orion.css                 (devtools/orion.css)
         skin/classic/aero/browser/devtools/orion-container.css       (devtools/orion-container.css)
         skin/classic/aero/browser/devtools/orion-task.png            (devtools/orion-task.png)
         skin/classic/aero/browser/devtools/orion-breakpoint.png      (devtools/orion-breakpoint.png)
         skin/classic/aero/browser/devtools/orion-debug-location.png  (devtools/orion-debug-location.png)
         skin/classic/aero/browser/devtools/toolbarbutton-close.png   (devtools/toolbarbutton-close.png)