Bug 656666 - Update HudService.jsm to allow GCLI integration; r=msucan,dao,l10n
authorJoe Walker <jwalker@mozilla.com>
Fri, 27 May 2011 13:49:02 +0100
changeset 78676 c41530066c721d8ead5b86eee1b9523442d26a47
parent 78675 9247eee791fd21d853887547a0898c6c2c3f1909
child 78677 967846102da6869143a432c387069d6cbfb1d5f7
push id21322
push userrcampbell@mozilla.com
push dateThu, 13 Oct 2011 16:40:40 +0000
treeherdermozilla-central@6d3a63f03f88 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmsucan, dao, l10n
bugs656666
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 656666 - Update HudService.jsm to allow GCLI integration; r=msucan,dao,l10n
browser/app/profile/firefox.js
browser/devtools/webconsole/GcliCommands.jsm
browser/devtools/webconsole/HUDService.jsm
browser/devtools/webconsole/Makefile.in
browser/devtools/webconsole/test/browser/Makefile.in
browser/devtools/webconsole/test/browser/browser_gcli_integrate.js
browser/devtools/webconsole/test/browser/browser_gcli_require.js
browser/devtools/webconsole/test/browser/browser_webconsole_gcli_require.js
browser/locales/en-US/chrome/browser/devtools/gcli.properties
browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
browser/locales/jar.mn
browser/themes/gnomestripe/browser/devtools/gcli.css
browser/themes/pinstripe/browser/devtools/gcli.css
browser/themes/winstripe/browser/devtools/gcli.css
browser/themes/winstripe/browser/jar.mn
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1005,16 +1005,19 @@ pref("devtools.inspector.enabled", true)
 pref("devtools.styleinspector.enabled", true);
 
 // Enable the Scratchpad tool.
 pref("devtools.scratchpad.enabled", true);
 
 // Enable tools for Chrome development.
 pref("devtools.chrome.enabled", false);
 
+// Disable the GCLI enhanced command line.
+pref("devtools.gcli.enable", false);
+
 // The last Web Console height. This is initially 0 which means that the Web
 // Console will use the default height next time it shows.
 // Change to -1 if you do not want the Web Console to remember its last height.
 pref("devtools.hud.height", 0);
 
 // Remember the Web Console position. Possible values:
 //   above - above the web page,
 //   below - below the web page,
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/GcliCommands.jsm
@@ -0,0 +1,178 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is GCLI Commands.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Joe Walker <jwalker@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+let EXPORTED_SYMBOLS = [ "GcliCommands" ];
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource:///modules/gcli.jsm");
+Components.utils.import("resource:///modules/HUDService.jsm");
+
+let bundleName = "chrome://browser/locale/devtools/gclicommands.properties";
+let stringBundle = Services.strings.createBundle(bundleName);
+
+let gcli = gcli._internal.require("gcli/index");
+let canon = gcli._internal.require("gcli/canon");
+
+
+let document;
+
+/**
+ * The exported API
+ */
+let GcliCommands = {
+  /**
+   * Allow HUDService to inform us of the document against which we work
+   */
+  setDocument: function GcliCommands_setDocument(aDocument) {
+    document = aDocument;
+  },
+
+  /**
+   * Undo the effects of GcliCommands.setDocument()
+   */
+  unsetDocument: function GcliCommands_unsetDocument() {
+    document = undefined;
+  }
+};
+
+
+/**
+ * Lookup a string in the GCLI string bundle
+ * @param aName The name to lookup
+ * @return The looked up name
+ */
+function lookup(aName)
+{
+  try {
+    return stringBundle.GetStringFromName(aName);
+  } catch (ex) {
+    throw new Error("Failure in lookup('" + aName + "')");
+  }
+};
+
+/**
+ * Lookup a string in the GCLI string bundle
+ * @param aName The name to lookup
+ * @param aSwaps An array of swaps. See stringBundle.formatStringFromName
+ * @return The looked up name
+ */
+function lookupFormat(aName, aSwaps)
+{
+  try {
+    return stringBundle.formatStringFromName(aName, aSwaps, aSwaps.length);
+  } catch (ex) {
+    Services.console.logStringMessage("Failure in lookupFormat('" + aName + "')");
+    throw new Error("Failure in lookupFormat('" + aName + "')");
+  }
+}
+
+
+/**
+ * 'echo' command
+ */
+gcli.addCommand({
+  name: "echo",
+  description: lookup("echoDesc"),
+  params: [
+    {
+      name: "message",
+      type: "string",
+      description: lookup("echoMessageDesc")
+    }
+  ],
+  returnType: "string",
+  exec: function Command_echo(args, context) {
+    return args.message;
+  }
+});
+
+
+/**
+ * 'help' command
+ */
+gcli.addCommand({
+  name: "help",
+  returnType: "html",
+  description: lookup("helpDesc"),
+  exec: function Command_help(args, context) {
+    let output = [];
+
+    output.push("<strong>" + lookup("helpAvailable") + ":</strong><br/>");
+
+    let commandNames = canon.getCommandNames();
+    commandNames.sort();
+
+    output.push("<table>");
+    for (let i = 0; i < commandNames.length; i++) {
+      let command = canon.getCommand(commandNames[i]);
+      if (!command.hidden && command.description) {
+        output.push("<tr>");
+        output.push('<th class="gcliCmdHelpRight">' + command.name + "</th>");
+        output.push("<td>&#x2192; " + command.description + "</td>");
+        output.push("</tr>");
+      }
+    }
+    output.push("</table>");
+
+    return output.join("");
+  }
+});
+
+
+/**
+ * 'console' command
+ */
+gcli.addCommand({
+  name: "console",
+  description: lookup("consoleDesc"),
+  manual: lookup("consoleManual")
+});
+
+/**
+ * 'console clear' command
+ */
+gcli.addCommand({
+  name: "console clear",
+  description: lookup("consoleclearDesc"),
+  exec: function(args, context) {
+    let hud = HUDService.getHudReferenceById(context.environment.hudId);
+    hud.gcliterm.clearOutput();
+  }
+});
+
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -23,16 +23,17 @@
  * Contributor(s):
  *   David Dahl <ddahl@mozilla.com> (original author)
  *   Rob Campbell <rcampbell@mozilla.com>
  *   Johnathan Nightingale <jnightingale@mozilla.com>
  *   Patrick Walton <pcwalton@mozilla.com>
  *   Julian Viereck <jviereck@mozilla.com>
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *   Michael Ratcliffe <mratcliffe@mozilla.com>
+ *   Joe Walker <jwalker@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -67,16 +68,28 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "mimeService",
                                    "@mozilla.org/mime;1",
                                    "nsIMIMEService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
                                    "@mozilla.org/widget/clipboardhelper;1",
                                    "nsIClipboardHelper");
 
+XPCOMUtils.defineLazyGetter(this, "gcli", function () {
+  var obj = {};
+  Cu.import("resource:///modules/gcli.jsm", obj);
+  return obj.gcli;
+});
+
+XPCOMUtils.defineLazyGetter(this, "GcliCommands", function () {
+  var obj = {};
+  Cu.import("resource:///modules/GcliCommands.jsm", obj);
+  return obj.GcliCommands;
+});
+
 XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () {
   var obj = {};
   Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj);
   return obj.StyleInspector;
 });
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function () {
   var obj = {};
@@ -1476,16 +1489,34 @@ HUD_SERVICE.prototype =
     this.windowInitializer(window);
 
     let hudId = "hud_" + nBox.id;
     let hudRef = this.hudReferences[hudId];
 
     if (!aAnimated || hudRef.consolePanel) {
       this.disableAnimation(hudId);
     }
+
+    // Create a processing instruction for GCLIs CSS stylesheet, but only if
+    // we don't have one for this document. Also record the context we're
+    // adding this for so we know when to remove it.
+    let procInstr = aContext.ownerDocument.gcliCssProcInstr;
+    if (!procInstr) {
+      procInstr = aContext.ownerDocument.createProcessingInstruction(
+              "xml-stylesheet",
+              "href='chrome://browser/skin/devtools/gcli.css' type='text/css'");
+      procInstr.contexts = [];
+
+      let root = aContext.ownerDocument.getElementsByTagName('window')[0];
+      root.parentNode.insertBefore(procInstr, root);
+      aContext.ownerDocument.gcliCssProcInstr = procInstr;
+    }
+    if (procInstr.contexts.indexOf(hudId) == -1) {
+      procInstr.contexts.push(hudId);
+    }
   },
 
   /**
    * Deactivate a HeadsUpDisplay for the given tab context.
    *
    * @param nsIDOMWindow aContext
    * @param aAnimated animate closing the web console?
    * @returns void
@@ -1507,16 +1538,30 @@ HUD_SERVICE.prototype =
       let hud = this.hudReferences[hudId];
       browser.webProgress.removeProgressListener(hud.progressListener);
       delete hud.progressListener;
 
       this.unregisterDisplay(hudId);
 
       window.focus();
     }
+
+    // Remove this context from the list of contexts that need the GCLI CSS
+    // processing instruction and then remove the processing instruction if it
+    // isn't needed any more.
+    let procInstr = aContext.ownerDocument.gcliCssProcInstr;
+    if (procInstr) {
+      procInstr.contexts = procInstr.contexts.filter(function(id) {
+        return id !== hudId;
+      });
+      if (procInstr.contexts.length == 0 && procInstr.parentNode) {
+        procInstr.parentNode.removeChild(procInstr);
+        delete aContext.ownerDocument.gcliCssProcInstr;
+      }
+    }
   },
 
   /**
    * get a unique ID from the sequence generator
    *
    * @returns integer
    */
   sequenceId: function HS_sequencerId()
@@ -1745,17 +1790,22 @@ HUD_SERVICE.prototype =
    */
   unregisterDisplay: function HS_unregisterDisplay(aHUDId)
   {
     let hud = this.getHudReferenceById(aHUDId);
 
     // Remove children from the output. If the output is not cleared, there can
     // be leaks as some nodes has node.onclick = function; set and GC can't
     // remove the nodes then.
-    hud.jsterm.clearOutput();
+    if (hud.jsterm) {
+      hud.jsterm.clearOutput();
+    }
+    if (hud.gcliterm) {
+      hud.gcliterm.clearOutput();
+    }
 
     hud.destroy();
 
     // Make sure that the console panel does not try to call
     // deactivateHUDForContext() again.
     hud.consoleWindowUnregisterOnHide = false;
 
     // Remove the HUDBox and the consolePanel if the Web Console is inside a
@@ -1764,17 +1814,19 @@ HUD_SERVICE.prototype =
     if (hud.consolePanel) {
       hud.consolePanel.parentNode.removeChild(hud.consolePanel);
     }
 
     if (hud.splitter.parentNode) {
       hud.splitter.parentNode.removeChild(hud.splitter);
     }
 
-    hud.jsterm.autocompletePopup.destroy();
+    if (hud.jsterm) {
+      hud.jsterm.autocompletePopup.destroy();
+    }
 
     delete this.hudReferences[aHUDId];
 
     // remove the related storage object
     this.storage.removeDisplay(aHUDId);
 
     for (let windowID in this.windowIds) {
       if (this.windowIds[windowID] == aHUDId) {
@@ -3086,17 +3138,22 @@ function HeadsUpDisplay(aConfig)
 
   // create a panel dynamically and attach to the parentNode
   this.createHUD();
 
   this.HUDBox.lastTimestamp = 0;
   // create the JSTerm input element
   try {
     this.createConsoleInput(this.contentWindow, this.consoleWrap, this.outputNode);
-    this.jsterm.inputNode.focus();
+    if (this.jsterm) {
+      this.jsterm.inputNode.focus();
+    }
+    if (this.gcliterm) {
+      this.gcliterm.inputNode.focus();
+    }
   }
   catch (ex) {
     Cu.reportError(ex);
   }
 
   // A cache for tracking repeated CSS Nodes.
   this.cssNodes = {};
 }
@@ -3207,16 +3264,19 @@ HeadsUpDisplay.prototype = {
       // Scroll the outputNode back to the last location.
       if (lastIndex > -1 && lastIndex < this.outputNode.getRowCount()) {
         this.outputNode.ensureIndexIsVisible(lastIndex);
       }
 
       if (this.jsterm) {
         this.jsterm.inputNode.focus();
       }
+      if (this.gcliterm) {
+        this.gcliterm.inputNode.focus();
+      }
     }).bind(this);
 
     panel.addEventListener("popupshown", onPopupShown,false);
 
     let onPopupHidden = (function HUD_onPopupHidden(aEvent) {
       if (aEvent.target != panel) {
         return;
       }
@@ -3398,16 +3458,19 @@ HeadsUpDisplay.prototype = {
       // must destroy the consolePanel
       this.consoleWindowUnregisterOnHide = false;
       this.consolePanel.hidePopup();
     }
 
     if (this.jsterm) {
       this.jsterm.inputNode.focus();
     }
+    if (this.gcliterm) {
+      this.gcliterm.inputNode.focus();
+    }
   },
 
   /**
    * L10N shortcut function
    *
    * @param string aName
    * @returns string
    */
@@ -3430,30 +3493,47 @@ HeadsUpDisplay.prototype = {
 
   /**
    * The JSTerm object that contains the console's inputNode
    *
    */
   jsterm: null,
 
   /**
+   * The GcliTerm object that contains the console's GCLI
+   */
+  gcliterm: null,
+
+  /**
    * creates and attaches the console input node
    *
    * @param nsIDOMWindow aWindow
    * @returns void
    */
   createConsoleInput:
   function HUD_createConsoleInput(aWindow, aParentNode, aExistingConsole)
   {
-    var context = Cu.getWeakReference(aWindow);
+    let usegcli = false;
+    try {
+      usegcli = Services.prefs.getBoolPref("devtools.gcli.enable");
+    }
+    catch (ex) {}
 
     if (appName() == "FIREFOX") {
-      let mixin = new JSTermFirefoxMixin(context, aParentNode,
-                                         aExistingConsole);
-      this.jsterm = new JSTerm(context, aParentNode, mixin, this.console);
+      if (!usegcli) {
+        let context = Cu.getWeakReference(aWindow);
+        let mixin = new JSTermFirefoxMixin(context, aParentNode,
+                                           aExistingConsole);
+        this.jsterm = new JSTerm(context, aParentNode, mixin, this.console);
+      }
+      else {
+        this.gcliterm = new GcliTerm(aWindow, this.hudId, this.chromeDocument,
+                                     this.console, this.hintNode);
+        aParentNode.appendChild(this.gcliterm.element);
+      }
     }
     else {
       throw new Error("Unsupported Gecko Application");
     }
   },
 
   /**
    * Re-attaches a console when the contentWindow is recreated
@@ -3466,24 +3546,27 @@ HeadsUpDisplay.prototype = {
     this.contentWindow = aContentWindow;
     this.contentDocument = this.contentWindow.document;
     this.uriSpec = this.contentWindow.location.href;
 
     if (this.consolePanel) {
       this.consolePanel.label = this.getPanelTitle();
     }
 
-    if (!this.jsterm) {
-      this.createConsoleInput(this.contentWindow, this.consoleWrap, this.outputNode);
-    }
-    else {
+    if (this.jsterm) {
       this.jsterm.context = Cu.getWeakReference(this.contentWindow);
       this.jsterm.console = this.console;
       this.jsterm.createSandbox();
     }
+    else if (this.gcliterm) {
+      this.gcliterm.reattachConsole(this.contentWindow, this.console);
+    }
+    else {
+      this.createConsoleInput(this.contentWindow, this.consoleWrap, this.outputNode);
+    }
   },
 
   /**
    * Shortcut to make XUL nodes
    *
    * @param string aTag
    * @returns nsIDOMNode
    */
@@ -3517,24 +3600,16 @@ HeadsUpDisplay.prototype = {
     let consoleCommandSet = this.makeXULNode("commandset");
     outerWrap.appendChild(consoleCommandSet);
 
     let consoleWrap = this.makeXULNode("vbox");
     this.consoleWrap = consoleWrap;
     consoleWrap.setAttribute("class", "hud-console-wrapper");
     consoleWrap.setAttribute("flex", "1");
 
-    this.outputNode = this.makeXULNode("richlistbox");
-    this.outputNode.setAttribute("class", "hud-output-node");
-    this.outputNode.setAttribute("flex", "1");
-    this.outputNode.setAttribute("orient", "vertical");
-    this.outputNode.setAttribute("context", this.hudId + "-output-contextmenu");
-    this.outputNode.setAttribute("style", "direction: ltr;");
-    this.outputNode.setAttribute("seltype", "multiple");
-
     this.filterSpacer = this.makeXULNode("spacer");
     this.filterSpacer.setAttribute("flex", "1");
 
     this.filterBox = this.makeXULNode("textbox");
     this.filterBox.setAttribute("class", "compact hud-filter-box");
     this.filterBox.setAttribute("hudId", this.hudId);
     this.filterBox.setAttribute("placeholder", this.getStr("stringFilter"));
     this.filterBox.setAttribute("type", "search");
@@ -3543,19 +3618,46 @@ HeadsUpDisplay.prototype = {
 
     this.createConsoleMenu(this.consoleWrap);
 
     this.filterPrefs = HUDService.getDefaultFilterPrefs(this.hudId);
 
     let consoleFilterToolbar = this.makeFilterToolbar();
     consoleFilterToolbar.setAttribute("id", "viewGroup");
     this.consoleFilterToolbar = consoleFilterToolbar;
+
+    let hintSpacerNode = this.makeXULNode("box");
+    hintSpacerNode.setAttribute("flex", 1);
+
+    this.hintNode = this.makeXULNode("div");
+    this.hintNode.setAttribute("class", "gcliterm-hint-node");
+
+    let hintParentNode = this.makeXULNode("vbox");
+    hintParentNode.setAttribute("flex", "0");
+    hintParentNode.setAttribute("class", "gcliterm-hint-parent");
+    hintParentNode.appendChild(hintSpacerNode);
+    hintParentNode.appendChild(this.hintNode);
+    hintParentNode.hidden = true;
+
+    let hbox = this.makeXULNode("hbox");
+    hbox.setAttribute("flex", "1");
+
+    this.outputNode = this.makeXULNode("richlistbox");
+    this.outputNode.setAttribute("class", "hud-output-node");
+    this.outputNode.setAttribute("flex", "1");
+    this.outputNode.setAttribute("orient", "vertical");
+    this.outputNode.setAttribute("context", this.hudId + "-output-contextmenu");
+    this.outputNode.setAttribute("style", "direction: ltr;");
+    this.outputNode.setAttribute("seltype", "multiple");
+
+    hbox.appendChild(hintParentNode);
+    hbox.appendChild(this.outputNode);
+
     consoleWrap.appendChild(consoleFilterToolbar);
-
-    consoleWrap.appendChild(this.outputNode);
+    consoleWrap.appendChild(hbox);
 
     outerWrap.appendChild(consoleWrap);
 
     this.HUDBox.lastTimestamp = 0;
 
     this.jsTermParentNode = outerWrap;
     this.HUDBox.appendChild(outerWrap);
 
@@ -3860,17 +3962,23 @@ HeadsUpDisplay.prototype = {
    * @param string aHUDId
    *        The ID of the console.
    * @return void
    */
   makeClearConsoleButton: function HUD_makeClearConsoleButton(aToolbar)
   {
     let hudId = this.hudId;
     function HUD_clearButton_onCommand() {
-      HUDService.getHudReferenceById(hudId).jsterm.clearOutput();
+      let hud = HUDService.getHudReferenceById(hudId);
+      if (hud.jsterm) {
+        hud.jsterm.clearOutput();
+      }
+      if (hud.gcliterm) {
+        hud.gcliterm.clearOutput();
+      }
     }
 
     let clearButton = this.makeXULNode("toolbarbutton");
     clearButton.setAttribute("label", this.getStr("btnClear"));
     clearButton.classList.add("webconsole-clear-console-button");
     clearButton.addEventListener("command", HUD_clearButton_onCommand, false);
 
     aToolbar.appendChild(clearButton);
@@ -3931,17 +4039,22 @@ HeadsUpDisplay.prototype = {
   },
 
   /**
    * Destroy the HUD object. Call this method to avoid memory leaks when the Web
    * Console is closed.
    */
   destroy: function HUD_destroy()
   {
-    this.jsterm.destroy();
+    if (this.jsterm) {
+      this.jsterm.destroy();
+    }
+    if (this.gcliterm) {
+      this.gcliterm.destroy();
+    }
 
     this.positionMenuitems.above.removeEventListener("command",
       this._positionConsoleAbove, false);
     this.positionMenuitems.below.removeEventListener("command",
       this._positionConsoleBelow, false);
     this.positionMenuitems.window.removeEventListener("command",
       this._positionConsoleWindow, false);
 
@@ -5426,45 +5539,39 @@ JSTermFirefoxMixin.prototype = {
    * @returns void
    */
   generateUI: function JSTF_generateUI()
   {
     this.completeNode = this.xulElementFactory("textbox");
     this.completeNode.setAttribute("class", "jsterm-complete-node");
     this.completeNode.setAttribute("multiline", "true");
     this.completeNode.setAttribute("rows", "1");
+    this.completeNode.setAttribute("tabindex", "-1");
 
     this.inputNode = this.xulElementFactory("textbox");
     this.inputNode.setAttribute("class", "jsterm-input-node");
     this.inputNode.setAttribute("multiline", "true");
     this.inputNode.setAttribute("rows", "1");
 
     let inputStack = this.xulElementFactory("stack");
     inputStack.setAttribute("class", "jsterm-stack-node");
     inputStack.setAttribute("flex", "1");
     inputStack.appendChild(this.completeNode);
     inputStack.appendChild(this.inputNode);
 
     if (this.existingConsoleNode == undefined) {
-      this.outputNode = this.xulElementFactory("vbox");
-      this.outputNode.setAttribute("class", "jsterm-output-node");
-
-      this.term = this.xulElementFactory("vbox");
-      this.term.setAttribute("class", "jsterm-wrapper-node");
-      this.term.setAttribute("flex", "1");
-      this.term.appendChild(this.outputNode);
-    }
-    else {
-      this.outputNode = this.existingConsoleNode;
-
-      this.term = this.xulElementFactory("hbox");
-      this.term.setAttribute("class", "jsterm-input-container");
-      this.term.setAttribute("style", "direction: ltr;");
-      this.term.appendChild(inputStack);
-    }
+      throw new Error("This can't happen");
+    }
+
+    this.outputNode = this.existingConsoleNode;
+
+    this.term = this.xulElementFactory("hbox");
+    this.term.setAttribute("class", "jsterm-input-container");
+    this.term.setAttribute("style", "direction: ltr;");
+    this.term.appendChild(inputStack);
   },
 
   get inputValue()
   {
     return this.inputNode.value;
   },
 
   attachUI: function JSTF_attachUI()
@@ -5586,19 +5693,18 @@ ConsoleUtils = {
    * @return nsIDOMNode
    *         The message node: a XUL richlistitem ready to be inserted into
    *         the Web Console output node.
    */
   createMessageNode:
   function ConsoleUtils_createMessageNode(aDocument, aCategory, aSeverity,
                                           aBody, aHUDId, aSourceURL,
                                           aSourceLine, aClipboardText, aLevel) {
-    if (aBody instanceof Ci.nsIDOMNode && aClipboardText == null) {
-      throw new Error("HUDService.createMessageNode(): DOM node supplied " +
-                      "without any clipboard text");
+    if (typeof aBody != "string" && aClipboardText == null && aBody.innerText) {
+      aClipboardText = aBody.innerText;
     }
 
     // Make the icon container, which is a vertical box. Its purpose is to
     // ensure that the icon stays anchored at the top of the message even for
     // long multi-line messages.
     let iconContainer = aDocument.createElementNS(XUL_NS, "vbox");
     iconContainer.classList.add("webconsole-msg-icon-container");
     // Apply the curent group by indenting appropriately.
@@ -5627,16 +5733,23 @@ ConsoleUtils = {
     // If a string was supplied for the body, turn it into a DOM node and an
     // associated clipboard string now.
     aClipboardText = aClipboardText ||
                      (aBody + (aSourceURL ? " @ " + aSourceURL : "") +
                               (aSourceLine ? ":" + aSourceLine : ""));
     aBody = aBody instanceof Ci.nsIDOMNode && !(aLevel == "dir") ?
             aBody : aDocument.createTextNode(aBody);
 
+    if (!aBody.nodeType) {
+      aBody = aDocument.createTextNode(aBody.toString());
+    }
+    if (typeof aBody == "string") {
+      aBody = aDocument.createTextNode(aBody);
+    }
+
     bodyNode.appendChild(aBody);
 
     let repeatContainer = aDocument.createElementNS(XUL_NS, "hbox");
     repeatContainer.setAttribute("align", "start");
     let repeatNode = aDocument.createElementNS(XUL_NS, "label");
     repeatNode.setAttribute("value", "1");
     repeatNode.classList.add("webconsole-msg-repeat");
     repeatContainer.appendChild(repeatNode);
@@ -6756,8 +6869,240 @@ try {
   // *any* of this fails
   var HUDService = new HUD_SERVICE();
 }
 catch (ex) {
   Cu.reportError("HUDService failed initialization.\n" + ex);
   // TODO: kill anything that may have started up
   // see bug 568665
 }
+
+///////////////////////////////////////////////////////////////////////////
+// GcliTerm
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ * GcliTerm
+ *
+ * Initialize GCLI by creating a set of startup options from the available
+ * properties.
+ *
+ * @param nsIDOMWindow aContentWindow
+ *        The content window that we're providing as the context to commands
+ * @param string aHudId
+ *        The HUD to which we should send console messages.
+ * @param nsIDOMDocument aDocument
+ *        The DOM document from which to create nodes.
+ * @param object aConsole
+ *        Console object to use within the GcliTerm.
+ * @param nsIDOMElement aHintNode
+ *        The node to which we add GCLI's hints.
+ * @constructor
+ */
+function GcliTerm(aContentWindow, aHudId, aDocument, aConsole, aHintNode)
+{
+  this.context = Cu.getWeakReference(aContentWindow);
+  this.hudId = aHudId;
+  this.document = aDocument;
+  this.console = aConsole;
+  this.hintNode = aHintNode;
+
+  this.createUI();
+  this.createSandbox();
+
+  this.show = this.show.bind(this);
+  this.hide = this.hide.bind(this);
+
+  this.opts = {
+    environment: { hudId: this.hudId },
+    chromeDocument: this.document,
+    contentDocument: aContentWindow.document,
+    jsEnvironment: {
+      globalObject: aContentWindow,
+      evalFunction: this.evalInSandbox.bind(this)
+    },
+    inputElement: this.inputNode,
+    completeElement: this.completeNode,
+    inputBackgroundElement: this.inputStack,
+    hintElement: this.hintNode,
+    completionPrompt: "",
+    gcliTerm: this
+  };
+
+  gcli._internal.commandOutputManager.addListener(this.onCommandOutput, this);
+  gcli._internal.createView(this.opts);
+  GcliCommands.setDocument(aContentWindow.document);
+}
+
+GcliTerm.prototype = {
+  /**
+   * Remove the hint column from the display.
+   */
+  hide: function GcliTerm_hide()
+  {
+    this.hintNode.parentNode.hidden = true;
+  },
+
+  /**
+   * Undo the effects of calling hide().
+   */
+  show: function GcliTerm_show()
+  {
+    this.hintNode.parentNode.hidden = false;
+  },
+
+  /**
+   * Destroy the GcliTerm object. Call this method to avoid memory leaks.
+   */
+  destroy: function Gcli_destroy()
+  {
+    GcliCommands.unsetDocument();
+    gcli._internal.removeView(this.opts);
+    gcli._internal.commandOutputManager.removeListener(this.onCommandOutput, this);
+
+    delete this.opts.chromeDocument;
+    delete this.opts.inputElement;
+    delete this.opts.completeElement;
+    delete this.opts.inputBackgroundElement;
+    delete this.opts.hintElement;
+    delete this.opts.contentDocument;
+    delete this.opts.jsEnvironment;
+    delete this.opts.gcliTerm;
+
+    delete this.context;
+    delete this.document;
+    delete this.console;
+    delete this.hintNode;
+
+    delete this.sandbox;
+    delete this.element
+    delete this.inputStack
+    delete this.completeNode
+    delete this.inputNode
+  },
+
+  /**
+   * Re-attaches a console when the contentWindow is recreated.
+   *
+   * @param nsIDOMWindow aContentWindow
+   *        The content window that we're providing as the context to commands
+   * @param object aConsole
+   *        Console object to use within the GcliTerm.
+   */
+  reattachConsole: function Gcli_reattachConsole(aContentWindow, aConsole)
+  {
+    this.context = Cu.getWeakReference(aContentWindow);
+    this.console = aConsole;
+    this.createSandbox();
+  },
+
+  /**
+   * Generates and attaches the GCLI Terminal part of the Web Console, which
+   * essentially consists of the interactive JavaScript input facility.
+   */
+  createUI: function Gcli_createUI()
+  {
+    this.element = this.document.createElement("vbox");
+    this.element.setAttribute("class", "gcliterm-input-container");
+    this.element.setAttribute("flex", "0");
+
+    this.inputStack = this.document.createElement("stack");
+    this.inputStack.setAttribute("class", "gcliterm-stack-node");
+    this.element.appendChild(this.inputStack);
+
+    this.completeNode = this.document.createElement("div");
+    this.completeNode.setAttribute("class", "gcliterm-complete-node");
+    this.completeNode.setAttribute("aria-live", "polite");
+    this.inputStack.appendChild(this.completeNode);
+
+    this.inputNode = this.document.createElement("textbox");
+    this.inputNode.setAttribute("class", "gcliterm-input-node");
+    this.inputNode.setAttribute("rows", "1");
+    this.inputStack.appendChild(this.inputNode);
+  },
+
+  /**
+   * Called by GCLI/canon when command line output changes.
+   */
+  onCommandOutput: function Gcli_onCommandOutput(aEvent)
+  {
+    // When we can update the history of the console, then we should stop
+    // filtering incomplete reports.
+    if (!aEvent.output.completed) {
+      return;
+    }
+
+    this.writeOutput(aEvent.output.typed, { category: CATEGORY_INPUT });
+
+    if (aEvent.output.output == null) {
+      return;
+    }
+
+    let output = aEvent.output.output;
+    if (aEvent.output.command.returnType == "html" && typeof output == "string") {
+      let frag = this.document.createRange().createContextualFragment(
+          '<div xmlns="' + HTML_NS + '" xmlns:xul="' + XUL_NS + '">' +
+          output + '</div>');
+
+      output = this.document.createElementNS(HTML_NS, "div");
+      output.appendChild(frag);
+    }
+    this.writeOutput(output);
+  },
+
+  /**
+   * Setup the eval sandbox, should be called whenever we are attached.
+   */
+  createSandbox: function Gcli_createSandbox()
+  {
+    let win = this.context.get().QueryInterface(Ci.nsIDOMWindow);
+
+    // create a JS Sandbox out of this.context
+    this.sandbox = new Cu.Sandbox(win, {
+      sandboxPrototype: win,
+      wantXrays: false
+    });
+    this.sandbox.console = this.console;
+  },
+
+  /**
+   * Evaluates a string in the sandbox.
+   *
+   * @param string aString
+   *        String to evaluate in the sandbox
+   * @return The result of the evaluation
+   */
+  evalInSandbox: function Gcli_evalInSandbox(aString)
+  {
+    return Cu.evalInSandbox(aString, this.sandbox, "1.8", "Web Console", 1);
+  },
+
+  /**
+   * Writes a message to the HUD that originates from the interactive
+   * JavaScript console.
+   *
+   * @param string aOutputMessage
+   *        The message to display.
+   * @param number aCategory
+   *        One of the CATEGORY_ constants.
+   * @param number aSeverity
+   *        One of the SEVERITY_ constants.
+   */
+  writeOutput: function Gcli_writeOutput(aOutputMessage, aOptions)
+  {
+    aOptions = aOptions || {};
+
+    let node = ConsoleUtils.createMessageNode(
+                    this.document,
+                    aOptions.category || CATEGORY_OUTPUT,
+                    aOptions.severity || SEVERITY_LOG,
+                    aOutputMessage,
+                    this.hudId,
+                    aOptions.sourceUrl || undefined,
+                    aOptions.sourceLine || undefined,
+                    aOptions.clipboardText || undefined);
+
+    ConsoleUtils.outputMessageNode(node, this.hudId);
+  },
+
+  clearOutput: JSTerm.prototype.clearOutput,
+};
+
--- a/browser/devtools/webconsole/Makefile.in
+++ b/browser/devtools/webconsole/Makefile.in
@@ -44,16 +44,17 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
 		PropertyPanel.jsm \
 		NetworkHelper.jsm \
 		AutocompletePopup.jsm \
 		gcli.jsm \
+		GcliCommands.jsm \
 		$(NULL)
 
 EXTRA_PP_JS_MODULES = \
 		HUDService.jsm \
 		$(NULL)
 
 ifdef ENABLE_TESTS
 ifneq (mobile,$(MOZ_BUILD_APP))
--- a/browser/devtools/webconsole/test/browser/Makefile.in
+++ b/browser/devtools/webconsole/test/browser/Makefile.in
@@ -103,17 +103,16 @@ include $(topsrcdir)/config/rules.mk
 	browser_webconsole_bug_592442_closing_brackets.js \
 	browser_webconsole_bug_593003_iframe_wrong_hud.js \
 	browser_webconsole_bug_601909_remember_height.js \
 	browser_webconsole_bug_613013_console_api_iframe.js \
 	browser_webconsole_bug_597756_reopen_closed_tab.js \
 	browser_webconsole_bug_600183_charset.js \
 	browser_webconsole_bug_601177_log_levels.js \
 	browser_webconsole_bug_597460_filter_scroll.js \
-	browser_webconsole_gcli_require.js \
 	browser_webconsole_console_extras.js \
 	browser_webconsole_bug_598357_jsterm_output.js \
 	browser_webconsole_bug_603750_websocket.js \
 	browser_webconsole_abbreviate_source_url.js \
 	browser_webconsole_view_source.js \
 	browser_webconsole_bug_602572_log_bodies_checkbox.js \
 	browser_webconsole_bug_614793_jsterm_scroll.js \
 	browser_webconsole_bug_599725_response_headers.js \
@@ -141,16 +140,18 @@ include $(topsrcdir)/config/rules.mk
 	browser_webconsole_bug_585991_autocomplete_keys.js \
 	browser_webconsole_bug_663443_panel_title.js \
 	browser_webconsole_bug_660806_history_nav.js \
 	browser_webconsole_bug_651501_document_body_autocomplete.js \
 	browser_webconsole_bug_653531_highlighter_console_helper.js \
 	browser_webconsole_bug_659907_console_dir.js \
 	browser_webconsole_bug_678816.js \
 	browser_webconsole_bug_664131_console_group.js \
+	browser_gcli_require.js \
+	browser_gcli_integrate.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	test-console.html \
 	test-network.html \
 	test-network-request.html \
 	test-mutation.html \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser/browser_gcli_integrate.js
@@ -0,0 +1,103 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// For more information on GCLI see:
+// - https://github.com/mozilla/gcli/blob/master/docs/index.md
+// - https://wiki.mozilla.org/DevTools/Features/GCLI
+
+// Tests that source URLs in the Web Console can be clicked to display the
+// standard View Source window.
+
+Components.utils.import("resource:///modules/gcli.jsm");
+let require = gcli._internal.require;
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/browser/test-console.html";
+
+registerCleanupFunction(function() {
+  require = undefined;
+  Services.prefs.clearUserPref("devtools.gcli.enable");
+});
+
+function test() {
+  Services.prefs.setBoolPref("devtools.gcli.enable", true);
+  addTab(TEST_URI);
+  browser.addEventListener("DOMContentLoaded", onLoad, false);
+}
+
+function onLoad() {
+  browser.removeEventListener("DOMContentLoaded", onLoad, false);
+
+  try {
+    openConsole();
+
+    testCreateCommands();
+    testCallCommands();
+    testRemoveCommands();
+  }
+  catch (ex) {
+    gcli._internal.console.error('Test Failure', ex);
+  }
+  finally {
+    closeConsole();
+    finishTest();
+  }
+}
+
+let tselarr = {
+  name: 'tselarr',
+  params: [
+    { name: 'num', type: { name: 'selection', data: [ '1', '2', '3' ] } },
+    { name: 'arr', type: { name: 'array', subtype: 'string' } },
+  ],
+  exec: function(args, env) {
+    return "flu " + args.num + "-" + args.arr.join("_");
+  }
+};
+
+function testCreateCommands() {
+  let gcli = require("gcli/index");
+  gcli.addCommand(tselarr);
+
+  let canon = require("gcli/canon");
+  let tselcmd = canon.getCommand("tselarr");
+  ok(tselcmd != null, "tselarr exists in the canon");
+  ok(tselcmd instanceof canon.Command, "canon storing commands");
+}
+
+function testCallCommands() {
+  let hud = HUDService.getHudByWindow(content);
+  let gcliterm = hud.gcliterm;
+  ok(gcliterm, "We have a GCLI term");
+
+  // Test successful auto-completion
+  gcliterm.inputNode.value = "h";
+  gcliterm.inputNode.focus();
+  EventUtils.synthesizeKey("e", {});
+  is(gcliterm.completeNode.textContent, " help", "Completion for \"he\"");
+
+  // Test unsuccessful auto-completion
+  gcliterm.inputNode.value = "ec";
+  gcliterm.inputNode.focus();
+  EventUtils.synthesizeKey("d", {});
+  is(gcliterm.completeNode.textContent, " ecd", "Completion for \"ecd\"");
+
+  // Test a normal command's life cycle
+  gcliterm.opts.inputter.setInput("echo hello world");
+  gcliterm.opts.requisition.exec();
+
+  let nodes = hud.outputNode.querySelectorAll("description");
+
+  is(nodes.length, 2, "Right number of output nodes");
+  ok(/hello world/.test(nodes[0].textContent), "the command's output is correct.");
+
+  gcliterm.clearOutput();
+}
+
+function testRemoveCommands() {
+  let gcli = require("gcli/index");
+  gcli.removeCommand(tselarr);
+
+  let canon = require("gcli/canon");
+  let tselcmd = canon.getCommand("tselarr");
+  ok(tselcmd == null, "tselcmd removed from the canon");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser/browser_gcli_require.js
@@ -0,0 +1,126 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// For more information on GCLI see:
+// - https://github.com/mozilla/gcli/blob/master/docs/index.md
+// - https://wiki.mozilla.org/DevTools/Features/GCLI
+
+// Tests that source URLs in the Web Console can be clicked to display the
+// standard View Source window.
+
+var modules = { gcli: null };
+
+Components.utils.import("resource:///modules/gcli.jsm", modules);
+
+var define, require, console;
+
+function test() {
+
+  define = modules.gcli._internal.define;
+  require = modules.gcli._internal.require;
+  console = modules.gcli._internal.console;
+
+  define('gclitest/requirable', [], function(require, exports, module) {
+    exports.thing1 = 'thing1';
+    exports.thing2 = 2;
+
+    let status = 'initial';
+    exports.setStatus = function(aStatus) { status = aStatus; };
+    exports.getStatus = function() { return status; };
+  });
+
+  define('gclitest/unrequirable', [], function(require, exports, module) {
+    null.throwNPE();
+  });
+
+  define('gclitest/recurse', [], function(require, exports, module) {
+    require('gclitest/recurse');
+  });
+
+  testWorking();
+  testLeakage();
+  testMultiImport();
+  testRecursive();
+  testUncompilable();
+
+  finishTest();
+
+  delete define.modules['gclitest/requirable'];
+  delete define.globalDomain.modules['gclitest/requirable'];
+  delete define.modules['gclitest/unrequirable'];
+  delete define.globalDomain.modules['gclitest/unrequirable'];
+  delete define.modules['gclitest/recurse'];
+  delete define.globalDomain.modules['gclitest/recurse'];
+
+  define = null;
+  require = null;
+  console = null;
+
+  modules = null;
+}
+
+function testWorking() {
+  // There are lots of requirement tests that we could be doing here
+  // The fact that we can get anything at all working is a testament to
+  // require doing what it should - we don't need to test the
+  let requireable = require('gclitest/requirable');
+  ok('thing1' == requireable.thing1, 'thing1 was required');
+  ok(2 == requireable.thing2, 'thing2 was required');
+  ok(requireable.thing3 === undefined, 'thing3 was not required');
+}
+
+function testDomains() {
+  let requireable = require('gclitest/requirable');
+  ok(requireable.status === undefined, 'requirable has no status');
+  requireable.setStatus(null);
+  ok(null === requireable.getStatus(), 'requirable.getStatus changed to null');
+  ok(requireable.status === undefined, 'requirable still has no status');
+  requireable.setStatus('42');
+  ok('42' == requireable.getStatus(), 'requirable.getStatus changed to 42');
+  ok(requireable.status === undefined, 'requirable *still* has no status');
+
+  let domain = new define.Domain();
+  let requireable2 = domain.require('gclitest/requirable');
+  ok(requireable2.status === undefined, 'requirable2 has no status');
+  ok('initial' === requireable2.getStatus(), 'requirable2.getStatus is initial');
+  requireable2.setStatus(999);
+  ok(999 === requireable2.getStatus(), 'requirable2.getStatus changed to 999');
+  ok(requireable2.status === undefined, 'requirable2 still has no status');
+
+  t.verifyEqual('42', requireable.getStatus());
+  ok(requireable.status === undefined, 'requirable has no status (as expected)');
+
+  delete domain.modules['gclitest/requirable'];
+}
+
+function testLeakage() {
+  let requireable = require('gclitest/requirable');
+  ok(requireable.setup == null, 'leakage of setup');
+  ok(requireable.shutdown == null, 'leakage of shutdown');
+  ok(requireable.testWorking == null, 'leakage of testWorking');
+}
+
+function testMultiImport() {
+  let r1 = require('gclitest/requirable');
+  let r2 = require('gclitest/requirable');
+  ok(r1 === r2, 'double require was strict equal');
+}
+
+function testUncompilable() {
+  // It's not totally clear how a module loader should perform with unusable
+  // modules, however at least it should go into a flat spin ...
+  // GCLI mini_require reports an error as it should
+  try {
+    let unrequireable = require('gclitest/unrequirable');
+    fail();
+  }
+  catch (ex) {
+    // an exception is expected
+  }
+}
+
+function testRecursive() {
+  // See Bug 658583
+  // require('gclitest/recurse');
+  // Also see the comments in the testRecursive() function
+}
deleted file mode 100644
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_gcli_require.js
+++ /dev/null
@@ -1,122 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-// Tests that source URLs in the Web Console can be clicked to display the
-// standard View Source window.
-
-var modules = { gcli: null };
-
-Components.utils.import("resource:///modules/gcli.jsm", modules);
-
-var define, require, console;
-
-function test() {
-
-  define = modules.gcli._internal.define;
-  require = modules.gcli._internal.require;
-  console = modules.gcli._internal.console;
-
-  define('gclitest/requirable', [], function(require, exports, module) {
-    exports.thing1 = 'thing1';
-    exports.thing2 = 2;
-
-    let status = 'initial';
-    exports.setStatus = function(aStatus) { status = aStatus; };
-    exports.getStatus = function() { return status; };
-  });
-
-  define('gclitest/unrequirable', [], function(require, exports, module) {
-    null.throwNPE();
-  });
-
-  define('gclitest/recurse', [], function(require, exports, module) {
-    require('gclitest/recurse');
-  });
-
-  testWorking();
-  testLeakage();
-  testMultiImport();
-  testRecursive();
-  testUncompilable();
-
-  finishTest();
-
-  delete define.modules['gclitest/requirable'];
-  delete define.globalDomain.modules['gclitest/requirable'];
-  delete define.modules['gclitest/unrequirable'];
-  delete define.globalDomain.modules['gclitest/unrequirable'];
-  delete define.modules['gclitest/recurse'];
-  delete define.globalDomain.modules['gclitest/recurse'];
-
-  define = null;
-  require = null;
-  console = null;
-
-  modules = null;
-}
-
-function testWorking() {
-  // There are lots of requirement tests that we could be doing here
-  // The fact that we can get anything at all working is a testament to
-  // require doing what it should - we don't need to test the
-  let requireable = require('gclitest/requirable');
-  ok('thing1' == requireable.thing1, 'thing1 was required');
-  ok(2 == requireable.thing2, 'thing2 was required');
-  ok(requireable.thing3 === undefined, 'thing3 was not required');
-}
-
-function testDomains() {
-  let requireable = require('gclitest/requirable');
-  ok(requireable.status === undefined, 'requirable has no status');
-  requireable.setStatus(null);
-  ok(null === requireable.getStatus(), 'requirable.getStatus changed to null');
-  ok(requireable.status === undefined, 'requirable still has no status');
-  requireable.setStatus('42');
-  ok('42' == requireable.getStatus(), 'requirable.getStatus changed to 42');
-  ok(requireable.status === undefined, 'requirable *still* has no status');
-
-  let domain = new define.Domain();
-  let requireable2 = domain.require('gclitest/requirable');
-  ok(requireable2.status === undefined, 'requirable2 has no status');
-  ok('initial' === requireable2.getStatus(), 'requirable2.getStatus is initial');
-  requireable2.setStatus(999);
-  ok(999 === requireable2.getStatus(), 'requirable2.getStatus changed to 999');
-  ok(requireable2.status === undefined, 'requirable2 still has no status');
-
-  t.verifyEqual('42', requireable.getStatus());
-  ok(requireable.status === undefined, 'requirable has no status (as expected)');
-
-  delete domain.modules['gclitest/requirable'];
-}
-
-function testLeakage() {
-  let requireable = require('gclitest/requirable');
-  ok(requireable.setup == null, 'leakage of setup');
-  ok(requireable.shutdown == null, 'leakage of shutdown');
-  ok(requireable.testWorking == null, 'leakage of testWorking');
-}
-
-function testMultiImport() {
-  let r1 = require('gclitest/requirable');
-  let r2 = require('gclitest/requirable');
-  ok(r1 === r2, 'double require was strict equal');
-}
-
-function testUncompilable() {
-  // It's not totally clear how a module loader should perform with unusable
-  // modules, however at least it should go into a flat spin ...
-  // GCLI mini_require reports an error as it should
-  try {
-    let unrequireable = require('gclitest/unrequirable');
-    fail();
-  }
-  catch (ex) {
-    // an exception is expected
-  }
-}
-
-function testRecursive() {
-  // See Bug 658583
-  // require('gclitest/recurse');
-  // Also see the comments in the testRecursive() function
-}
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/devtools/gcli.properties
@@ -0,0 +1,92 @@
+# LOCALIZATION NOTE These strings are used inside the Web Console
+# command line which is available from the Web Developer sub-menu
+# -> 'Web Console'.
+# The correct localization of this file might be to keep it in
+# English, or another language commonly spoken among web developers.
+# You want to make that choice consistent across the developer tools.
+# A good criteria is the language in which you'd find the best
+# documentation on web development on the web.
+
+# LOCALIZATION NOTE (canonDescNone): Short string used to describe any command
+# or command parameter when no description has been provided.
+canonDescNone=(No description)
+
+# LOCALIZATION NOTE (cliEvalJavascript): The special '{' command allows entry
+# of JavaScript like traditional developer tool command lines. This describes
+# the '{' command.
+cliEvalJavascript=Enter JavaScript directly
+
+# LOCALIZATION NOTE (fieldSelectionSelect): When a command has a parameter
+# that has a number of pre-defined options the user interface presents these
+# in a drop-down menu, where the first 'option' is an indicator that a
+# selection should be made. This string describes that first option.
+fieldSelectionSelect=Select a %S ...
+
+# LOCALIZATION NOTE (fieldArrayAdd): When a command has a parameter that can
+# be repeated a number of times (e.g. like the 'cat a.txt b.txt' command) the
+# user interface presents buttons to add and remove arguments. This string is
+# used to add arguments.
+fieldArrayAdd=Add
+
+# LOCALIZATION NOTE (fieldArrayDel): When a command has a parameter that can
+# be repeated a number of times (e.g. like the 'cat a.txt b.txt' command) the
+# user interface presents buttons to add and remove arguments. This string is
+# used to remove arguments.
+fieldArrayDel=Delete
+
+# LOCALIZATION NOTE (jstypeParseScope): The command line provides completion
+# for JavaScript commands, however there are times when the scope of what
+# we're completing against can't be used. This error message is displayed when
+# this happens.
+jstypeParseScope=Scope lost
+
+# LOCALIZATION NOTE (jstypeParseMissing): When the command line is doing
+# JavaScript completion, sometimes the property to be completed does not
+# exist. This error message is displayed when this happens.
+jstypeParseMissing=Can't find property '%S'
+
+# LOCALIZATION NOTE (jstypeBeginSyntax): When the command line is doing
+# JavaScript completion using invalid JavaScript, this error message is
+# displayed.
+jstypeBeginSyntax=Syntax error
+
+# LOCALIZATION NOTE (jstypeBeginUnterm): When the command line is doing
+# JavaScript completion using a string that is not properly terminated, this
+# error message is displayed.
+jstypeBeginUnterm=Unterminated string literal
+
+# LOCALIZATION NOTE (typesNumberNan): When the command line is passed a
+# number, however the input string is not a valid number, this error message
+# is displayed.
+typesNumberNan=Can't convert "%S" to a number.
+
+# LOCALIZATION NOTE (typesNumberMax): When the command line is passed a
+# number, but the number is bigger than the largest allowed number, this error
+# message is displayed.
+typesNumberMax=%1$S is greater that maximum allowed: %2$S.
+
+# LOCALIZATION NOTE (typesNumberMin): When the command line is passed a
+# number, but the number is lower than the smallest allowed number, this error
+# message is displayed.
+typesNumberMin=%1$S is smaller that minimum allowed: %2$S.
+
+# LOCALIZATION NOTE (typesSelectionNomatch): When the command line is passed
+# an option with a limited number of correct values, but the passed value is
+# not one of them, this error message is displayed.
+typesSelectionNomatch=Can't use '%S'.
+
+# LOCALIZATION NOTE (nodeParseSyntax): When the command line is expecting a
+# CSS query string, however the passed string is not valid, this error message
+# is displayed.
+nodeParseSyntax=Syntax error in CSS query
+
+# LOCALIZATION NOTE (nodeParseMultiple): When the command line is expecting a
+# CSS string that matches a single node, but more than one node matches, this
+# error message is displayed.
+nodeParseMultiple=Too many matches (%S)
+
+# LOCALIZATION NOTE (nodeParseNone): When the command line is expecting a CSS
+# string that matches a single node, but no nodes match, this error message is
+# displayed.
+nodeParseNone=No matches
+
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -0,0 +1,37 @@
+# LOCALIZATION NOTE These strings are used inside Web Console commands.
+# The Web Console command line is available from the Web Developer sub-menu
+# -> 'Web Console'.
+#
+# The correct localization of this file might be to keep it in
+# English, or another language commonly spoken among web developers.
+# You want to make that choice consistent across the developer tools.
+# A good criteria is the language in which you'd find the best
+# documentation on web development on the web.
+
+# LOCALIZATION NOTE (echoDesc) A very short string used to describe the
+# function of the echo command.
+echoDesc=Show a message
+
+# LOCALIZATION NOTE (echoMessageDesc) A very short string used to describe the
+# message parameter to the echo command.
+echoMessageDesc=Message
+
+# LOCALIZATION NOTE (helpDesc) A very short string used to describe the
+# function of the help command.
+helpDesc=Get help on the available commands
+
+# LOCALIZATION NOTE (helpAvailable) Used in the output of the help command to
+# explain the contents of the command help table.
+helpAvailable=Available Commands
+
+# LOCALIZATION NOTE (consoleDesc) A very short string used to describe the
+# function of the console command.
+consoleDesc=Commands to control the console
+
+# LOCALIZATION NOTE (consoleManual) A longer description describing the
+# set of commands that control the console.
+consoleManual=Filter, clear and close the web console
+
+# LOCALIZATION NOTE (consoleclearDesc) A very short string used to describe the
+# function of the 'console clear' command.
+consoleclearDesc=Clear the console
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -9,16 +9,18 @@
     locale/browser/aboutHome.dtd                   (%chrome/browser/aboutHome.dtd)
     locale/browser/aboutSessionRestore.dtd         (%chrome/browser/aboutSessionRestore.dtd)
 #ifdef MOZ_SERVICES_SYNC
     locale/browser/aboutSyncTabs.dtd               (%chrome/browser/aboutSyncTabs.dtd)
 #endif
 *   locale/browser/browser.dtd                     (%chrome/browser/browser.dtd)
     locale/browser/baseMenuOverlay.dtd             (%chrome/browser/baseMenuOverlay.dtd)
     locale/browser/browser.properties              (%chrome/browser/browser.properties)
+    locale/browser/devtools/gcli.properties         (%chrome/browser/devtools/gcli.properties)
+    locale/browser/devtools/gclicommands.properties (%chrome/browser/devtools/gclicommands.properties)
     locale/browser/styleinspector.properties       (%chrome/browser/styleinspector.properties)
     locale/browser/styleinspector.dtd              (%chrome/browser/styleinspector.dtd)
     locale/browser/scratchpad.properties           (%chrome/browser/scratchpad.properties)
     locale/browser/scratchpad.dtd                  (%chrome/browser/scratchpad.dtd)
     locale/browser/inspector.properties            (%chrome/browser/inspector.properties)
     locale/browser/openLocation.dtd                (%chrome/browser/openLocation.dtd)
     locale/browser/openLocation.properties         (%chrome/browser/openLocation.properties)
 *   locale/browser/pageInfo.dtd                    (%chrome/browser/pageInfo.dtd)
new file mode 100644
--- /dev/null
+++ b/browser/themes/gnomestripe/browser/devtools/gcli.css
@@ -0,0 +1,241 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the GCLI.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Joe Walker <jwalker@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* From: $GCLI/mozilla/gcli/ui/gcliterm.css */
+.gcliterm-input-node,
+.gcliterm-complete-node {
+  border: none;
+  -moz-appearance: none;
+  height: 100%;
+  vertical-align: middle;
+  background-color: transparent;
+  font: 12px Consolas, "Lucida Console", monospace;
+  padding: 2px 0 0 16px;
+}
+
+.gcliterm-complete-node {
+  color: #FFF;
+  padding: 4px 4px 2px 21px;
+}
+
+.gcliVALID {
+  border-bottom: none;
+}
+
+.gcliINCOMPLETE {
+  color: #DDD;
+  border-bottom: 1px dotted #999;
+}
+
+.gcliERROR {
+  color: #DDD;
+  border-bottom: 1px dotted #F00;
+}
+
+.gcliCompl {
+  color: #999;
+}
+
+.gcliterm-stack-node {
+  background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat;
+  width: 100%;
+}
+
+.gcliterm-hint-node {
+  color: #000;
+  background: rgba(250, 250, 250, 0.8);
+  border: 1px solid #AAA;
+  border-bottom: 0px !important;
+  border-top-right-radius: 5px;
+  border-top-left-radius: 5px;
+  margin-left: 10px;
+  margin-right: 10px;
+  display: inline-block;
+  overflow: auto;
+  padding: 10px;
+}
+
+.gcliterm-hint-parent {
+  border-bottom: 1px solid #AAA;
+}
+
+.gcliCmdHelpRight {
+  text-align: right;
+}
+
+/* From: $GCLI/lib/gcli/ui/arg_fetch.css */
+.gcliCmdDesc {
+  font-weight: bold;
+  text-align: center;
+  margin-bottom: 5px;
+  border-bottom: 1px solid #ddd;
+  padding-bottom: 3px;
+}
+
+.gcliParamGroup {
+  font-weight: bold;
+}
+
+.gcliParamName {
+  text-align: right;
+  font-size: 90%;
+}
+
+.gcliParamError {
+  font-size: 80%;
+  color: #900;
+}
+
+.gcliParamSubmit {
+  text-align: right;
+}
+
+.gcliGroupSymbol {
+  font-size: 90%;
+  color: #666;
+}
+
+.gcliRequired {
+  font-size: 80%;
+  color: #666;
+}
+
+.gcliParams {
+  width: 100%;
+}
+
+/* From: $GCLI/lib/gcli/ui/hinter.css */
+.gcliHintParent {
+  color: #000;
+  background: rgba(250, 250, 250, 0.8);
+  border: 1px solid #AAA;
+  border-top-right-radius: 5px;
+  border-top-left-radius: 5px;
+  margin-left: 10px;
+  margin-right: 10px;
+  display: inline-block;
+  overflow: hidden;
+}
+
+.gcliHints {
+  overflow: auto;
+  padding: 10px;
+  display: inline-block;
+}
+
+.gcliHints ul {
+  margin: 0;
+  padding: 0 15px;
+}
+
+/* From: $GCLI/lib/gcli/ui/menu.css */
+.gcliOption {
+  overflow: hidden;
+  white-space: nowrap;
+  cursor: pointer;
+  padding: 2px;
+}
+
+.gcliOption:hover {
+  background-color: rgb(230, 230, 230);
+}
+
+.gcliOptionName {
+  padding-right: 5px;
+}
+
+.gcliOptionDesc {
+  font-size: 80%;
+  color: #999;
+}
+
+.gcliMenuError {
+  overflow: hidden;
+  white-space: nowrap;
+  padding: 8px 2px 2px 2px;
+  font-size: 80%;
+  color: red;
+}
+
+.gcliMenuField {
+  background-color: white;
+  color: black;
+  border: 1px solid #aaa;
+  padding: 2px;
+  max-height: 300px;
+  overflow-y: auto;
+  max-width: 220px;
+  overflow-x: hidden;
+  margin: 0px 10px;
+  border-top: 0px !important;
+  border-bottom-right-radius: 5px;
+  border-bottom-left-radius: 5px;
+}
+
+/* From: $GCLI/lib/gcli/ui/inputter.css */
+.gcliCompletion {
+  position: absolute;
+  z-index: -1000;
+  background-color: #DDD;
+  border: 1px transparent solid;
+  padding: 1px 1px 1px 2px;
+}
+
+.gcliCompletion {
+  color: #DDD;
+}
+
+.gcliINCOMPLETE {
+  border-bottom: 2px dotted #999;
+}
+
+.gcliERROR {
+  border-bottom: 2px dotted #F00;
+}
+
+.gcliPrompt {
+  color: #66F;
+  font-weight: bold;
+}
+
+.gcliCompl {
+  color: #999;
+}
+
+.gcliCloseBrace {
+  color: #999;
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/pinstripe/browser/devtools/gcli.css
@@ -0,0 +1,241 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the GCLI.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Joe Walker <jwalker@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* From: $GCLI/mozilla/gcli/ui/gcliterm.css */
+.gcliterm-input-node,
+.gcliterm-complete-node {
+  border: none;
+  -moz-appearance: none;
+  height: 100%;
+  vertical-align: middle;
+  background-color: transparent;
+  font: 12px Consolas, "Lucida Console", monospace;
+  padding: 2px 0 0 16px;
+}
+
+.gcliterm-complete-node {
+  color: #FFF;
+  padding: 4px 4px 2px 21px;
+}
+
+.gcliVALID {
+  border-bottom: none;
+}
+
+.gcliINCOMPLETE {
+  color: #DDD;
+  border-bottom: 1px dotted #999;
+}
+
+.gcliERROR {
+  color: #DDD;
+  border-bottom: 1px dotted #F00;
+}
+
+.gcliCompl {
+  color: #999;
+}
+
+.gcliterm-stack-node {
+  background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat;
+  width: 100%;
+}
+
+.gcliterm-hint-node {
+  color: #000;
+  background: rgba(250, 250, 250, 0.8);
+  border: 1px solid #AAA;
+  border-bottom: 0px !important;
+  border-top-right-radius: 5px;
+  border-top-left-radius: 5px;
+  margin-left: 10px;
+  margin-right: 10px;
+  display: inline-block;
+  overflow: auto;
+  padding: 10px;
+}
+
+.gcliterm-hint-parent {
+  border-bottom: 1px solid #AAA;
+}
+
+.gcliCmdHelpRight {
+  text-align: right;
+}
+
+/* From: $GCLI/lib/gcli/ui/arg_fetch.css */
+.gcliCmdDesc {
+  font-weight: bold;
+  text-align: center;
+  margin-bottom: 5px;
+  border-bottom: 1px solid #ddd;
+  padding-bottom: 3px;
+}
+
+.gcliParamGroup {
+  font-weight: bold;
+}
+
+.gcliParamName {
+  text-align: right;
+  font-size: 90%;
+}
+
+.gcliParamError {
+  font-size: 80%;
+  color: #900;
+}
+
+.gcliParamSubmit {
+  text-align: right;
+}
+
+.gcliGroupSymbol {
+  font-size: 90%;
+  color: #666;
+}
+
+.gcliRequired {
+  font-size: 80%;
+  color: #666;
+}
+
+.gcliParams {
+  width: 100%;
+}
+
+/* From: $GCLI/lib/gcli/ui/hinter.css */
+.gcliHintParent {
+  color: #000;
+  background: rgba(250, 250, 250, 0.8);
+  border: 1px solid #AAA;
+  border-top-right-radius: 5px;
+  border-top-left-radius: 5px;
+  margin-left: 10px;
+  margin-right: 10px;
+  display: inline-block;
+  overflow: hidden;
+}
+
+.gcliHints {
+  overflow: auto;
+  padding: 10px;
+  display: inline-block;
+}
+
+.gcliHints ul {
+  margin: 0;
+  padding: 0 15px;
+}
+
+/* From: $GCLI/lib/gcli/ui/menu.css */
+.gcliOption {
+  overflow: hidden;
+  white-space: nowrap;
+  cursor: pointer;
+  padding: 2px;
+}
+
+.gcliOption:hover {
+  background-color: rgb(230, 230, 230);
+}
+
+.gcliOptionName {
+  padding-right: 5px;
+}
+
+.gcliOptionDesc {
+  font-size: 80%;
+  color: #999;
+}
+
+.gcliMenuError {
+  overflow: hidden;
+  white-space: nowrap;
+  padding: 8px 2px 2px 2px;
+  font-size: 80%;
+  color: red;
+}
+
+.gcliMenuField {
+  background-color: white;
+  color: black;
+  border: 1px solid #aaa;
+  padding: 2px;
+  max-height: 300px;
+  overflow-y: auto;
+  max-width: 220px;
+  overflow-x: hidden;
+  margin: 0px 10px;
+  border-top: 0px !important;
+  border-bottom-right-radius: 5px;
+  border-bottom-left-radius: 5px;
+}
+
+/* From: $GCLI/lib/gcli/ui/inputter.css */
+.gcliCompletion {
+  position: absolute;
+  z-index: -1000;
+  background-color: #DDD;
+  border: 1px transparent solid;
+  padding: 1px 1px 1px 2px;
+}
+
+.gcliCompletion {
+  color: #DDD;
+}
+
+.gcliINCOMPLETE {
+  border-bottom: 2px dotted #999;
+}
+
+.gcliERROR {
+  border-bottom: 2px dotted #F00;
+}
+
+.gcliPrompt {
+  color: #66F;
+  font-weight: bold;
+}
+
+.gcliCompl {
+  color: #999;
+}
+
+.gcliCloseBrace {
+  color: #999;
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/winstripe/browser/devtools/gcli.css
@@ -0,0 +1,241 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the GCLI.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Joe Walker <jwalker@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* From: $GCLI/mozilla/gcli/ui/gcliterm.css */
+.gcliterm-input-node,
+.gcliterm-complete-node {
+  border: none;
+  -moz-appearance: none;
+  height: 100%;
+  vertical-align: middle;
+  background-color: transparent;
+  font: 12px Consolas, "Lucida Console", monospace;
+  padding: 2px 0 0 16px;
+}
+
+.gcliterm-complete-node {
+  color: #FFF;
+  padding: 4px 4px 2px 21px;
+}
+
+.gcliVALID {
+  border-bottom: none;
+}
+
+.gcliINCOMPLETE {
+  color: #DDD;
+  border-bottom: 1px dotted #999;
+}
+
+.gcliERROR {
+  color: #DDD;
+  border-bottom: 1px dotted #F00;
+}
+
+.gcliCompl {
+  color: #999;
+}
+
+.gcliterm-stack-node {
+  background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat;
+  width: 100%;
+}
+
+.gcliterm-hint-node {
+  color: #000;
+  background: rgba(250, 250, 250, 0.8);
+  border: 1px solid #AAA;
+  border-bottom: 0px !important;
+  border-top-right-radius: 5px;
+  border-top-left-radius: 5px;
+  margin-left: 10px;
+  margin-right: 10px;
+  display: inline-block;
+  overflow: auto;
+  padding: 10px;
+}
+
+.gcliterm-hint-parent {
+  border-bottom: 1px solid #AAA;
+}
+
+.gcliCmdHelpRight {
+  text-align: right;
+}
+
+/* From: $GCLI/lib/gcli/ui/arg_fetch.css */
+.gcliCmdDesc {
+  font-weight: bold;
+  text-align: center;
+  margin-bottom: 5px;
+  border-bottom: 1px solid #ddd;
+  padding-bottom: 3px;
+}
+
+.gcliParamGroup {
+  font-weight: bold;
+}
+
+.gcliParamName {
+  text-align: right;
+  font-size: 90%;
+}
+
+.gcliParamError {
+  font-size: 80%;
+  color: #900;
+}
+
+.gcliParamSubmit {
+  text-align: right;
+}
+
+.gcliGroupSymbol {
+  font-size: 90%;
+  color: #666;
+}
+
+.gcliRequired {
+  font-size: 80%;
+  color: #666;
+}
+
+.gcliParams {
+  width: 100%;
+}
+
+/* From: $GCLI/lib/gcli/ui/hinter.css */
+.gcliHintParent {
+  color: #000;
+  background: rgba(250, 250, 250, 0.8);
+  border: 1px solid #AAA;
+  border-top-right-radius: 5px;
+  border-top-left-radius: 5px;
+  margin-left: 10px;
+  margin-right: 10px;
+  display: inline-block;
+  overflow: hidden;
+}
+
+.gcliHints {
+  overflow: auto;
+  padding: 10px;
+  display: inline-block;
+}
+
+.gcliHints ul {
+  margin: 0;
+  padding: 0 15px;
+}
+
+/* From: $GCLI/lib/gcli/ui/menu.css */
+.gcliOption {
+  overflow: hidden;
+  white-space: nowrap;
+  cursor: pointer;
+  padding: 2px;
+}
+
+.gcliOption:hover {
+  background-color: rgb(230, 230, 230);
+}
+
+.gcliOptionName {
+  padding-right: 5px;
+}
+
+.gcliOptionDesc {
+  font-size: 80%;
+  color: #999;
+}
+
+.gcliMenuError {
+  overflow: hidden;
+  white-space: nowrap;
+  padding: 8px 2px 2px 2px;
+  font-size: 80%;
+  color: red;
+}
+
+.gcliMenuField {
+  background-color: white;
+  color: black;
+  border: 1px solid #aaa;
+  padding: 2px;
+  max-height: 300px;
+  overflow-y: auto;
+  max-width: 220px;
+  overflow-x: hidden;
+  margin: 0px 10px;
+  border-top: 0px !important;
+  border-bottom-right-radius: 5px;
+  border-bottom-left-radius: 5px;
+}
+
+/* From: $GCLI/lib/gcli/ui/inputter.css */
+.gcliCompletion {
+  position: absolute;
+  z-index: -1000;
+  background-color: #DDD;
+  border: 1px transparent solid;
+  padding: 1px 1px 1px 2px;
+}
+
+.gcliCompletion {
+  color: #DDD;
+}
+
+.gcliINCOMPLETE {
+  border-bottom: 2px dotted #999;
+}
+
+.gcliERROR {
+  border-bottom: 2px dotted #F00;
+}
+
+.gcliPrompt {
+  color: #66F;
+  font-weight: bold;
+}
+
+.gcliCompl {
+  color: #999;
+}
+
+.gcliCloseBrace {
+  color: #999;
+}
--- a/browser/themes/winstripe/browser/jar.mn
+++ b/browser/themes/winstripe/browser/jar.mn
@@ -104,16 +104,17 @@ browser.jar:
         skin/classic/browser/tabview/search.png                     (tabview/search.png)
         skin/classic/browser/tabview/stack-expander.png             (tabview/stack-expander.png)
         skin/classic/browser/tabview/tabview.png                    (tabview/tabview.png)
         skin/classic/browser/tabview/tabview-inverted.png           (tabview/tabview-inverted.png)
         skin/classic/browser/tabview/tabview.css                    (tabview/tabview.css)
         skin/classic/browser/devtools/arrows.png                    (devtools/arrows.png)
         skin/classic/browser/devtools/search.png                    (devtools/search.png)
         skin/classic/browser/devtools/csshtmltree.css               (devtools/csshtmltree.css)
+        skin/classic/browser/devtools/gcli.css                      (devtools/gcli.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/sync-throbber.png
         skin/classic/browser/sync-16.png
         skin/classic/browser/sync-32.png
         skin/classic/browser/sync-bg.png
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-mobileIcon.png
         skin/classic/browser/sync-notification-24.png
@@ -227,16 +228,17 @@ browser.jar:
         skin/classic/aero/browser/tabview/search.png                 (tabview/search.png)
         skin/classic/aero/browser/tabview/stack-expander.png         (tabview/stack-expander.png)
         skin/classic/aero/browser/tabview/tabview.png                (tabview/tabview.png)
         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/arrows.png                (devtools/arrows.png)
         skin/classic/aero/browser/devtools/search.png                (devtools/search.png)
         skin/classic/aero/browser/devtools/csshtmltree.css           (devtools/csshtmltree.css)
+        skin/classic/aero/browser/devtools/gcli.css                  (devtools/gcli.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/sync-throbber.png
         skin/classic/aero/browser/sync-16.png
         skin/classic/aero/browser/sync-32.png
         skin/classic/aero/browser/sync-bg.png
         skin/classic/aero/browser/sync-desktopIcon.png
         skin/classic/aero/browser/sync-mobileIcon.png
         skin/classic/aero/browser/sync-notification-24.png