Merge m-c to fx-team
authorPanos Astithas <past@mozilla.com>
Wed, 16 Jan 2013 10:13:29 +0200
changeset 119044 d8be4bc4fba886dc37544ca1b3bf984df4cb9dde
parent 119033 cc60821ec21ce1ac22e84b6a07f9f1a699c9688a (current diff)
parent 119043 50ea8eac36c25bfc0afb74f7357348f73a9c38a1 (diff)
child 119045 8d8912467c41bc824cfb3746e4515e16b7aba85c
child 119217 efd6dc4cd8251586d4549e266319839a660bd6c9
push id21433
push useremorley@mozilla.com
push dateWed, 16 Jan 2013 20:27:50 +0000
treeherdermozilla-inbound@c4b6301bee26 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone21.0a1
first release with
nightly linux32
d8be4bc4fba8 / 21.0a1 / 20130116031003 / files
nightly linux64
d8be4bc4fba8 / 21.0a1 / 20130116031003 / files
nightly mac
d8be4bc4fba8 / 21.0a1 / 20130116031003 / files
nightly win32
d8be4bc4fba8 / 21.0a1 / 20130116031003 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Merge m-c to fx-team
--- a/browser/devtools/commandline/test/head.js
+++ b/browser/devtools/commandline/test/head.js
@@ -62,10 +62,15 @@ function addTab(aURL, aCallback)
   return deferred.promise;
 }
 
 registerCleanupFunction(function tearDown() {
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 
-  console = undefined;
+  // Force GC, because it seems that GCLI can outrun the garbage collector
+  // in some situations, which causes test failures in later tests
+  // Bug 774619 is an example.
+  window.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDOMWindowUtils)
+      .garbageCollect();
 });
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -80,32 +80,25 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_bug727429_watch-expressions-01.js \
 	browser_dbg_bug727429_watch-expressions-02.js \
 	browser_dbg_bug731394_editor-contextmenu.js \
 	browser_dbg_bug786070_hide_nonenums.js \
 	browser_dbg_displayName.js \
 	browser_dbg_iframes.js \
 	browser_dbg_pause-exceptions.js \
 	browser_dbg_multiple-windows.js \
+	browser_dbg_bfcache.js \
 	browser_dbg_breakpoint-new-script.js \
 	browser_dbg_bug737803_editor_actual_location.js \
 	browser_dbg_progress-listener-bug.js \
 	browser_dbg_chrome-debugging.js \
 	$(filter disabled-for-intermittent-failures--bug-753225, browser_dbg_createRemote.js) \
 	head.js \
 	$(NULL)
 
-ifneq ($(OS_ARCH),WINNT)
-MOCHITEST_BROWSER_TESTS += \
-	browser_dbg_bfcache.js \
-	$(NULL)
-else
-$(filter disabled-temporarily--bug-774619, browser_dbg_bfcache.js)
-endif
-
 MOCHITEST_BROWSER_PAGES = \
 	browser_dbg_tab1.html \
 	browser_dbg_tab2.html \
 	browser_dbg_debuggerstatement.html \
 	browser_dbg_stack.html \
 	browser_dbg_script-switching.html \
 	test-script-switching-01.js \
 	test-script-switching-02.js \
--- a/browser/devtools/framework/Target.jsm
+++ b/browser/devtools/framework/Target.jsm
@@ -212,27 +212,29 @@ TabTarget.prototype = {
   /**
    * Listen to the different events.
    */
   _setupListeners: function TabTarget__setupListeners() {
     this._webProgressListener = new TabWebProgressListener(this);
     this.tab.linkedBrowser.addProgressListener(this._webProgressListener);
     this.tab.addEventListener("TabClose", this);
     this.tab.parentNode.addEventListener("TabSelect", this);
+    this.tab.ownerDocument.defaultView.addEventListener("close", this);
     this._handleThreadState = this._handleThreadState.bind(this);
     this.on("thread-resumed", this._handleThreadState);
     this.on("thread-paused", this._handleThreadState);
   },
 
   /**
    * Handle tabs events.
    */
   handleEvent: function (event) {
     switch (event.type) {
       case "TabClose":
+      case "close":
         this.destroy();
         break;
       case "TabSelect":
         if (this.tab.selected) {
           this.emit("visible", event);
         } else {
           this.emit("hidden", event);
         }
@@ -259,16 +261,17 @@ TabTarget.prototype = {
    */
   destroy: function() {
     if (!this._destroyed) {
       this._destroyed = true;
 
       this.tab.linkedBrowser.removeProgressListener(this._webProgressListener)
       this._webProgressListener.target = null;
       this._webProgressListener = null;
+      this.tab.ownerDocument.defaultView.removeEventListener("close", this);
       this.tab.removeEventListener("TabClose", this);
       this.tab.parentNode.removeEventListener("TabSelect", this);
       this.off("thread-resumed", this._handleThreadState);
       this.off("thread-paused", this._handleThreadState);
       this.emit("close");
 
       targets.delete(this._tab);
       this._tab = null;
--- a/browser/devtools/framework/Toolbox.jsm
+++ b/browser/devtools/framework/Toolbox.jsm
@@ -251,31 +251,65 @@ Toolbox.prototype = {
         this.isReady = true;
 
         let closeButton = this.doc.getElementById("toolbox-close");
         closeButton.addEventListener("command", this.destroy, true);
 
         this._buildDockButtons();
         this._buildTabs();
         this._buildButtons();
+        this._addKeysToWindow();
 
         this.selectTool(this._defaultToolId).then(function(panel) {
           this.emit("ready");
           deferred.resolve();
         }.bind(this));
       }.bind(this);
 
       iframe.addEventListener("DOMContentLoaded", domReady, true);
       iframe.setAttribute("src", this._URL);
     }.bind(this));
 
     return deferred.promise;
   },
 
   /**
+   * Adds the keys and commands to the Toolbox Window in window mode.
+   */
+  _addKeysToWindow: function TBOX__addKeysToWindow() {
+    if (this.hostType != Toolbox.HostType.WINDOW) {
+      return;
+    }
+    let doc = this.doc.defaultView.parent.document;
+    for (let [id, toolDefinition] of gDevTools._tools) {
+      if (toolDefinition.key) {
+        // Prevent multiple entries for the same tool.
+        if (doc.getElementById("key_" + id)) {
+          continue;
+        }
+        let key = doc.createElement("key");
+        key.id = "key_" + id;
+
+        if (toolDefinition.key.startsWith("VK_")) {
+          key.setAttribute("keycode", toolDefinition.key);
+        } else {
+          key.setAttribute("key", toolDefinition.key);
+        }
+
+        key.setAttribute("modifiers", toolDefinition.modifiers);
+        key.setAttribute("oncommand", "void(0);"); // needed. See bug 371900
+        key.addEventListener("command", function(toolId) {
+          this.selectTool(toolId);
+        }.bind(this, id), true);
+        doc.getElementById("toolbox-keyset").appendChild(key);
+      }
+    }
+  },
+
+  /**
    * Build the buttons for changing hosts. Called every time
    * the host changes.
    */
   _buildDockButtons: function TBOX_createDockButtons() {
     let dockBox = this.doc.getElementById("toolbox-dock-buttons");
 
     while (dockBox.firstChild) {
       dockBox.removeChild(dockBox.firstChild);
@@ -366,16 +400,18 @@ Toolbox.prototype = {
     }.bind(this, id));
 
     let vbox = this.doc.createElement("vbox");
     vbox.className = "toolbox-panel";
     vbox.id = "toolbox-panel-" + id;
 
     tabs.appendChild(radio);
     deck.appendChild(vbox);
+
+    this._addKeysToWindow();
   },
 
   /**
    * Switch to the tool with the given id
    *
    * @param {string} id
    *        The id of the tool to switch to
    */
@@ -515,16 +551,17 @@ Toolbox.prototype = {
       this._host.off("window-closed", this.destroy);
       this._host.destroy();
 
       this._host = newHost;
 
       Services.prefs.setCharPref(this._prefs.LAST_HOST, this._host.type);
 
       this._buildDockButtons();
+      this._addKeysToWindow();
 
       this.emit("host-changed");
     }.bind(this));
   },
 
   /**
    * Handler for the tool-registered event.
    * @param  {string} event
@@ -565,16 +602,24 @@ Toolbox.prototype = {
       }
       radio.parentNode.removeChild(radio);
     }
 
     if (panel) {
       panel.parentNode.removeChild(panel);
     }
 
+    if (this.hostType == Toolbox.HostType.WINDOW) {
+      let doc = this.doc.defaultView.parent.document;
+      let key = doc.getElementById("key_" + id);
+      if (key) {
+        key.parentNode.removeChild(key);
+      }
+    }
+
     if (this._toolPanels.has(toolId)) {
       let instance = this._toolPanels.get(toolId);
       instance.destroy();
       this._toolPanels.delete(toolId);
     }
   },
 
 
--- a/browser/devtools/framework/test/Makefile.in
+++ b/browser/devtools/framework/test/Makefile.in
@@ -16,11 +16,12 @@ MOCHITEST_BROWSER_FILES = \
 		browser_new_activation_workflow.js \
 		browser_toolbox_dynamic_registration.js \
 		browser_toolbox_hosts.js \
 		browser_toolbox_ready.js \
 		browser_toolbox_select_event.js \
 		browser_target_events.js \
 		browser_toolbox_tool_ready.js \
 		browser_toolbox_sidebar.js \
+		browser_toolbox_window_shortcuts.js \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let temp = {};
+Cu.import("resource:///modules/devtools/Toolbox.jsm", temp);
+let Toolbox = temp.Toolbox;
+temp = null;
+
+let toolbox, toolIDs, idIndex;
+
+function test() {
+  waitForExplicitFinish();
+
+  addTab("about:blank", function() {
+    toolIDs = [];
+    for (let [id, definition] of gDevTools._tools) {
+      if (definition.key) {
+        toolIDs.push(id);
+      }
+    }
+    let target = TargetFactory.forTab(gBrowser.selectedTab);
+    idIndex = 0;
+    gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.WINDOW)
+             .then(testShortcuts);
+  });
+}
+
+function testShortcuts(aToolbox, aIndex) {
+  if (aIndex == toolIDs.length) {
+    tidyUp();
+    return;
+  }
+
+  toolbox = aToolbox;
+  info("Toolbox fired a `ready` event");
+
+  toolbox.once("select", selectCB);
+
+  if (aIndex != null) {
+    // This if block is to allow the call of selectCB without shortcut press for
+    // the first time. That happens because on opening of toolbox, one tool gets
+    // selected atleast.
+
+    let key = gDevTools._tools.get(toolIDs[aIndex]).key;
+    let toolModifiers = gDevTools._tools.get(toolIDs[aIndex]).modifiers;
+    let modifiers = {
+      accelKey: toolModifiers.contains("accel"),
+      altKey: toolModifiers.contains("alt"),
+      shiftKey: toolModifiers.contains("shift"),
+    };
+    idIndex = aIndex;
+    info("Testing shortcut for tool " + aIndex + ":" + toolIDs[aIndex] +
+         " using key " + key);
+    EventUtils.synthesizeKey(key, modifiers, toolbox.doc.defaultView.parent);
+  }
+}
+
+function selectCB(event, id) {
+  info("toolbox-select event from " + id);
+
+  is(toolIDs.indexOf(id), idIndex,
+     "Correct tool is selected on pressing the shortcut for " + id);
+
+  testShortcuts(toolbox, idIndex + 1);
+}
+
+function tidyUp() {
+  toolbox.destroy();
+  gBrowser.removeCurrentTab();
+
+  toolbox = toolIDs = idIndex = Toolbox = null;
+  finish();
+}
--- a/browser/devtools/framework/toolbox-window.xul
+++ b/browser/devtools/framework/toolbox-window.xul
@@ -10,17 +10,17 @@
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="devtools-toolbox-window"
         title="&window.title;"
         macanimationtype="document"
         fullscreenbutton="true"
         windowtype="devtools:toolbox"
-        width="700" height="320"
+        width="900" height="320"
         persist="screenX screenY width height sizemode">
 
   <commandset id="toolbox-commandset">
     <command id="toolbox-cmd-close" oncommand="window.close();"/>
   </commandset>
 
   <keyset id="toolbox-keyset">
     <key id="toolbox-key-close"
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -988,138 +988,149 @@ WebConsoleFrame.prototype = {
    *        The message received from the server.
    * @return nsIDOMElement|undefined
    *         The message element to display in the Web Console output.
    */
   logConsoleAPIMessage: function WCF_logConsoleAPIMessage(aMessage)
   {
     let body = null;
     let clipboardText = null;
-    let sourceURL = null;
-    let sourceLine = 0;
+    let sourceURL = aMessage.filename;
+    let sourceLine = aMessage.lineNumber;
     let level = aMessage.level;
     let args = aMessage.arguments;
     let objectActors = [];
 
+    // Gather the actor IDs.
+    args.forEach(function(aValue) {
+      if (aValue && typeof aValue == "object" && aValue.actor) {
+        objectActors.push(aValue.actor);
+        let displayStringIsLong = typeof aValue.displayString == "object" &&
+                                  aValue.displayString.type == "longString";
+        if (displayStringIsLong) {
+          objectActors.push(aValue.displayString.actor);
+        }
+      }
+    }, this);
+
     switch (level) {
       case "log":
       case "info":
       case "warn":
       case "error":
       case "debug":
       case "dir":
       case "groupEnd": {
         body = { arguments: args };
         let clipboardArray = [];
         args.forEach(function(aValue) {
           clipboardArray.push(WebConsoleUtils.objectActorGripToString(aValue));
           if (aValue && typeof aValue == "object" && aValue.actor) {
-            objectActors.push(aValue.actor);
             let displayStringIsLong = typeof aValue.displayString == "object" &&
                                       aValue.displayString.type == "longString";
             if (aValue.type == "longString" || displayStringIsLong) {
               clipboardArray.push(l10n.getStr("longStringEllipsis"));
             }
-            if (displayStringIsLong) {
-              objectActors.push(aValue.displayString.actor);
-            }
           }
         }, this);
         clipboardText = clipboardArray.join(" ");
-        sourceURL = aMessage.filename;
-        sourceLine = aMessage.lineNumber;
 
         if (level == "dir") {
           body.objectProperties = aMessage.objectProperties;
         }
-        else if (level == "groupEnd") {
-          objectActors.forEach(this._releaseObject, this);
-
-          if (this.groupDepth > 0) {
-            this.groupDepth--;
-          }
-          return; // no need to continue
-        }
-
         break;
       }
 
       case "trace": {
-        let filename = WebConsoleUtils.abbreviateSourceURL(args[0].filename);
-        let functionName = args[0].functionName ||
+        let filename = WebConsoleUtils.abbreviateSourceURL(aMessage.filename);
+        let functionName = aMessage.functionName ||
                            l10n.getStr("stacktrace.anonymousFunction");
-        let lineNumber = args[0].lineNumber;
 
         body = l10n.getFormatStr("stacktrace.outputMessage",
-                                 [filename, functionName, lineNumber]);
-
-        sourceURL = args[0].filename;
-        sourceLine = args[0].lineNumber;
+                                 [filename, functionName, sourceLine]);
 
         clipboardText = "";
 
-        args.forEach(function(aFrame) {
+        aMessage.stacktrace.forEach(function(aFrame) {
           clipboardText += aFrame.filename + " :: " +
                            aFrame.functionName + " :: " +
                            aFrame.lineNumber + "\n";
         });
 
         clipboardText = clipboardText.trimRight();
         break;
       }
 
       case "group":
       case "groupCollapsed":
-        clipboardText = body = args;
-        sourceURL = aMessage.filename;
-        sourceLine = aMessage.lineNumber;
+        clipboardText = body = aMessage.groupName;
         this.groupDepth++;
         break;
 
-      case "time":
-        if (!args) {
+      case "time": {
+        let timer = aMessage.timer;
+        if (!timer) {
           return;
         }
-        if (args.error) {
-          Cu.reportError(l10n.getStr(args.error));
+        if (timer.error) {
+          Cu.reportError(l10n.getStr(timer.error));
           return;
         }
-        body = l10n.getFormatStr("timerStarted", [args.name]);
+        body = l10n.getFormatStr("timerStarted", [timer.name]);
         clipboardText = body;
-        sourceURL = aMessage.filename;
-        sourceLine = aMessage.lineNumber;
         break;
-
-      case "timeEnd":
-        if (!args) {
+      }
+
+      case "timeEnd": {
+        let timer = aMessage.timer;
+        if (!timer) {
           return;
         }
-        body = l10n.getFormatStr("timeEnd", [args.name, args.duration]);
+        body = l10n.getFormatStr("timeEnd", [timer.name, timer.duration]);
         clipboardText = body;
-        sourceURL = aMessage.filename;
-        sourceLine = aMessage.lineNumber;
         break;
+      }
 
       default:
         Cu.reportError("Unknown Console API log level: " + level);
         return;
     }
 
+    // Release object actors for arguments coming from console API methods that
+    // we ignore their arguments.
+    switch (level) {
+      case "group":
+      case "groupCollapsed":
+      case "groupEnd":
+      case "trace":
+      case "time":
+      case "timeEnd":
+        objectActors.forEach(this._releaseObject, this);
+        objectActors = [];
+    }
+
+    if (level == "groupEnd") {
+      if (this.groupDepth > 0) {
+        this.groupDepth--;
+      }
+      return; // no need to continue
+    }
+
     let node = this.createMessageNode(CATEGORY_WEBDEV, LEVELS[level], body,
                                       sourceURL, sourceLine, clipboardText,
                                       level, aMessage.timeStamp);
 
     if (objectActors.length) {
       node._objectActors = objectActors;
     }
 
     // Make the node bring up the property panel, to allow the user to inspect
     // the stack trace.
     if (level == "trace") {
-      node._stacktrace = args;
+      node._stacktrace = aMessage.stacktrace;
 
       this.makeOutputMessageLink(node, function _traceNodeClickCallback() {
         if (node._panelOpen) {
           return;
         }
 
         let options = {
           anchor: node,
--- a/browser/themes/gnomestripe/devtools/common.css
+++ b/browser/themes/gnomestripe/devtools/common.css
@@ -10,19 +10,20 @@
   font-family: monospace;
 }
 
 /* Toolbar and Toolbar items */
 
 .devtools-toolbar {
   -moz-appearance: none;
   padding: 4px 3px;
-  box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
-  background: -moz-linear-gradient(top, hsl(210,11%,36%), hsl(210,11%,18%));
   color: hsl(210,30%,85%);
+  background-image: url(background-noise-toolbar.png), linear-gradient(#3e4750, #3e4750);
+  border-bottom: 1px solid #060a0d;
+  box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset, -1px 0 0 hsla(204,45%,98%,.05) inset, 0 -1px 0 hsla(204,45%,98%,.05) inset;
 }
 
 .devtools-menulist,
 .devtools-toolbarbutton {
   -moz-appearance: none;
   -moz-box-align: center;
   min-width: 78px;
   min-height: 22px;
@@ -216,57 +217,112 @@
   -moz-appearance: none;
   padding: 0;
   border: 0;
 }
 
 .devtools-sidebar-tabs > tabs {
   -moz-appearance: none;
   position: static;
-  box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
-  background-image: linear-gradient(to bottom, hsl(210,11%,36%), hsl(210,11%,18%));
   color: hsl(210,30%,85%);
   margin-bottom: 0;
   padding: 0;
+  background-image: url(background-noise-toolbar.png), linear-gradient(#3e4750, #3e4750);
+  box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset, -1px 0 0 hsla(204,45%,98%,.05) inset, 0 -1px 0 hsla(204,45%,98%,.05) inset;
+  border-width: 0 0 1px 0;
+  border-color: hsla(210,8%,5%,.6);
+  border-style: solid;
+  overflow: hidden;
 }
 
 .devtools-sidebar-tabs > tabs > .tabs-right,
 .devtools-sidebar-tabs > tabs > .tabs-left {
   display: none;
 }
 
 .devtools-sidebar-tabs > tabs > tab {
   -moz-appearance: none;
-  padding: 0;
+  /* We want to match the height of a toolbar with a toolbarbutton 
+   * First, we need to replicated the padding of toolbar (4px),
+   * then, the padding of the button itself from toolbarbutton.css (3px),
+   * Also, we need to take the border of the buttons into accout (1px).
+   * Padding-bottom is one pixel shorter because we need to include the
+   * black border.
+   */
+  padding: 8px 3px 7px;
+  -moz-padding-start: 6px;
   margin: 0;
   min-width: 78px;
-  min-height: 22px;
   text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
   text-align: center;
   color: inherit;
   -moz-box-flex: 1;
   border-width: 0;
-  -moz-border-end-width: 1px;
-  border-color: hsla(210,8%,5%,.6);
-  border-style: solid;
   background: transparent;
   border-radius: 0;
+  position: static;
+  -moz-margin-start: -1px;
+}
+
+.devtools-sidebar-tabs > tabs > tab:first-of-type {
+  -moz-margin-start: -3px;
+}
+
+.devtools-sidebar-tabs > tabs > tab {
+  background-size: 100% 100%, 1px 100%, 1px 100%, 1px 100%;
+  background-repeat: no-repeat;
+  background-position: 2px, 0, 1px, 2px;
+}
+
+.devtools-sidebar-tabs:-moz-locale-dir(rtl) > tabs > tab {
+  background-position: calc(100% - 3px), 100%, calc(100% - 1px), calc(100% - 2px);
 }
 
-.devtools-sidebar-tabs > tabs > tab:-moz-focusring {
-  position: static;
+%filter substitution
+%define smallSeparator linear-gradient(hsla(204,45%,98%,0), hsla(204,45%,98%,.1), hsla(204,45%,98%,0)), linear-gradient(hsla(206,37%,4%,0), hsla(206,37%,4%,.6), hsla(206,37%,4%,0)), linear-gradient(hsla(204,45%,98%,0), hsla(204,45%,98%,.1), hsla(204,45%,98%,0))
+%define solidSeparator linear-gradient(transparent, transparent), linear-gradient(hsla(206,37%,4%,.6), hsla(206,37%,4%,.7)), linear-gradient(hsla(204,45%,98%,.1), hsla(204,45%,98%,.1))
+
+.devtools-sidebar-tabs > tabs > tab {
+  background-image: linear-gradient(transparent, transparent), @smallSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab:hover {
+  background-image: linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.2)), @smallSeparator@;
 }
 
-.devtools-sidebar-tabs > tabs > tab:last-of-type {
-  -moz-border-end-width: 0;
+.devtools-sidebar-tabs > tabs > tab:hover:active {
+  background-image: linear-gradient(hsla(206,37%,4%,.4), hsla(206,37%,4%,.4)), @smallSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab {
+  background-image: linear-gradient(transparent, transparent), @solidSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab:hover {
+  background-image: linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.2)), @solidSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab:hover:active {
+  background-image: linear-gradient(hsla(206,37%,4%,.4), hsla(206,37%,4%,.4)), @solidSeparator@;
 }
 
 .devtools-sidebar-tabs > tabs > tab[selected=true] {
-  background-image: linear-gradient(to bottom, hsl(201,45%,34%), hsl(205,44%,22%));
-  color: white !important;
+  color: #f5f7fa;
+  background-image: linear-gradient(#2f607b, #294d68), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.1) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true]:hover {
+  background-image: linear-gradient(#274f64, #224056), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true]:hover:active {
+  background-image: linear-gradient(#1f3e4f, #1b3243), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
 /* Theme */
 
 .devtools-theme-background {
   background-color: white;
 }
 
--- a/browser/themes/gnomestripe/devtools/layoutview.css
+++ b/browser/themes/gnomestripe/devtools/layoutview.css
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 body {
   background: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
   color: hsl(210,100%,85%);
-  border-top: 1px solid black;
   -moz-box-sizing: border-box;
 }
 
 #element-size {
   color: hsl(210,100%,95%);
 }
 
 #main {
--- a/browser/themes/gnomestripe/devtools/splitview.css
+++ b/browser/themes/gnomestripe/devtools/splitview.css
@@ -67,17 +67,16 @@
 }
 
 /* Toolbars */
 
 .devtools-toolbar {
   height: 26px;
   background-origin: border-box;
   background-clip: border-box;
-  border-top: 1px solid hsla(210,8%,5%,.5);
   border-bottom: 1px solid hsla(210,8%,5%,.65);
   padding: 3px;
 }
 
 .splitview-main > toolbar:-moz-locale-dir(ltr) {
   border-right: 1px solid hsla(210,8%,5%,.5);
 }
 
--- a/browser/themes/gnomestripe/devtools/toolbox.css
+++ b/browser/themes/gnomestripe/devtools/toolbox.css
@@ -116,17 +116,19 @@
 
 
 /* Tabs */
 
 .devtools-tabbar {
   -moz-appearance: none;
   background-image: url("background-noise-toolbar.png"),
                     linear-gradient(#303840, #2d3640);
-  border-top: 1px solid #060a0d;
+  border-color: #060a0d;
+  border-style: solid;
+  border-width: 1px 0;
   box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset,
               0 -1px 0 hsla(206,37%,4%,.1) inset;
   min-height: 32px;
   padding: 0;
 }
 
 #toolbox-tabs {
   margin: 0;
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -103,17 +103,17 @@ browser.jar:
   skin/classic/browser/tabbrowser/tab.png             (tabbrowser/tab.png)
   skin/classic/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
   skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/tabview/edit-light.png         (tabview/edit-light.png)
   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.css            (tabview/tabview.css)
-  skin/classic/browser/devtools/common.css            (devtools/common.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/command-responsivemode.png (devtools/command-responsivemode.png)
   skin/classic/browser/devtools/command-scratchpad.png (devtools/command-scratchpad.png)
   skin/classic/browser/devtools/command-tilt.png      (devtools/command-tilt.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)
@@ -155,16 +155,17 @@ browser.jar:
   skin/classic/browser/devtools/splitview.css         (devtools/splitview.css)
   skin/classic/browser/devtools/styleeditor.css       (devtools/styleeditor.css)
   skin/classic/browser/devtools/debugger.css          (devtools/debugger.css)
   skin/classic/browser/devtools/magnifying-glass.png  (devtools/magnifying-glass.png)
   skin/classic/browser/devtools/option-icon.png       (devtools/option-icon.png)
   skin/classic/browser/devtools/itemToggle.png        (devtools/itemToggle.png)
   skin/classic/browser/devtools/itemArrow-rtl.png     (devtools/itemArrow-rtl.png)
   skin/classic/browser/devtools/itemArrow-ltr.png     (devtools/itemArrow-ltr.png)
+  skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
   skin/classic/browser/devtools/inspect-button.png    (devtools/inspect-button.png)
   skin/classic/browser/devtools/dropmarker.png        (devtools/dropmarker.png)
   skin/classic/browser/devtools/layout-background-grid.png (devtools/layout-background-grid.png)
   skin/classic/browser/devtools/layoutview.css        (devtools/layoutview.css)
   skin/classic/browser/devtools/debugger-collapse.png  (devtools/debugger-collapse.png)
   skin/classic/browser/devtools/debugger-expand.png    (devtools/debugger-expand.png)
   skin/classic/browser/devtools/debugger-pause.png     (devtools/debugger-pause.png)
   skin/classic/browser/devtools/debugger-play.png      (devtools/debugger-play.png)
--- a/browser/themes/pinstripe/devtools/common.css
+++ b/browser/themes/pinstripe/devtools/common.css
@@ -12,19 +12,20 @@
   font-family: monospace;
 }
 
 /* Toolbar and Toolbar items */
 
 .devtools-toolbar {
   -moz-appearance: none;
   padding: 4px 3px;
-  box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
-  background-image: url(background-noise-toolbar.png), -moz-linear-gradient(top, hsl(210,11%,36%), hsl(210,11%,18%));
   color: hsl(210,30%,85%);
+  background-image: url(background-noise-toolbar.png), linear-gradient(#3e4750, #3e4750);
+  border-bottom: 1px solid #060a0d;
+  box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset, -1px 0 0 hsla(204,45%,98%,.05) inset, 0 -1px 0 hsla(204,45%,98%,.05) inset;
 }
 
 .devtools-menulist,
 .devtools-toolbarbutton {
   -moz-appearance: none;
   -moz-box-align: center;
   min-width: 78px;
   min-height: 22px;
@@ -228,55 +229,120 @@
 }
 
 .devtools-sidebar-tabs > tabpanels {
   padding: 0;
 }
 
 .devtools-sidebar-tabs > tabs {
   -moz-appearance: none;
+  font: inherit;
   position: static;
-  box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
-  background-image: url(background-noise-toolbar.png), linear-gradient(to bottom, hsl(210,11%,36%), hsl(210,11%,18%));
   color: hsl(210,30%,85%);
   margin-bottom: 0;
   padding: 0;
+  background-image: url(background-noise-toolbar.png), linear-gradient(#3e4750, #3e4750);
+  box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset, -1px 0 0 hsla(204,45%,98%,.05) inset, 0 -1px 0 hsla(204,45%,98%,.05) inset;
+  border-width: 0 0 1px 0;
+  border-color: hsla(210,8%,5%,.6);
+  border-style: solid;
+  overflow: hidden;
 }
 
 .devtools-sidebar-tabs > tabs > .tabs-right,
 .devtools-sidebar-tabs > tabs > .tabs-left {
   display: none;
 }
 
 .devtools-sidebar-tabs > tabs > tab {
   -moz-appearance: none;
+  /* We want to match the height of a toolbar with a toolbarbutton 
+   * First, we need to replicated the padding of toolbar (4px),
+   * then, the padding of the button itself from toolbarbutton.css (3px),
+   * Also, we need to take the border of the buttons into accout (1px).
+   * Minus the tab-text margin (2px).
+   * Padding-bottom is one pixel shorter because we need to include the
+   * black border.
+   */
+  padding: 6px 3px 5px !important;
+  -moz-padding-start: 6px;
   padding: 0;
   min-width: 78px;
-  min-height: 22px;
   text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
   text-align: center;
   color: inherit;
   -moz-box-flex: 1;
   border-width: 0;
-  -moz-border-end-width: 1px;
-  border-color: hsla(210,8%,5%,.6);
-  border-style: solid;
+  position: static;
+  -moz-margin-start: -1px;
 }
 
 .devtools-sidebar-tabs > tabs > tab:-moz-focusring {
   position: static;
 }
 
+.devtools-sidebar-tabs > tabs > tab:first-of-type {
+  -moz-margin-start: -3px;
+}
+
+.devtools-sidebar-tabs > tabs > tab {
+  background-size: 100% 100%, 1px 100%, 1px 100%, 1px 100%;
+  background-repeat: no-repeat;
+  background-position: 2px, 0, 1px, 2px;
+}
+
+.devtools-sidebar-tabs:-moz-locale-dir(rtl) > tabs > tab {
+  background-position: calc(100% - 3px), 100%, calc(100% - 1px), calc(100% - 2px);
+}
+
 .devtools-sidebar-tabs > tabs > tab:last-of-type {
   -moz-border-end-width: 0;
 }
 
+%define smallSeparator linear-gradient(hsla(204,45%,98%,0), hsla(204,45%,98%,.1), hsla(204,45%,98%,0)), linear-gradient(hsla(206,37%,4%,0), hsla(206,37%,4%,.6), hsla(206,37%,4%,0)), linear-gradient(hsla(204,45%,98%,0), hsla(204,45%,98%,.1), hsla(204,45%,98%,0))
+%define solidSeparator linear-gradient(transparent, transparent), linear-gradient(hsla(206,37%,4%,.6), hsla(206,37%,4%,.7)), linear-gradient(hsla(204,45%,98%,.1), hsla(204,45%,98%,.1))
+
+.devtools-sidebar-tabs > tabs > tab {
+  background-image: linear-gradient(transparent, transparent), @smallSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab:hover {
+  background-image: linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.2)), @smallSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab:hover:active {
+  background-image: linear-gradient(hsla(206,37%,4%,.4), hsla(206,37%,4%,.4)), @smallSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab {
+  background-image: linear-gradient(transparent, transparent), @solidSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab:hover {
+  background-image: linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.2)), @solidSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab:hover:active {
+  background-image: linear-gradient(hsla(206,37%,4%,.4), hsla(206,37%,4%,.4)), @solidSeparator@;
+}
+
 .devtools-sidebar-tabs > tabs > tab[selected=true] {
-  background-image: url(background-noise-toolbar.png), linear-gradient(to bottom, hsl(201,45%,34%), hsl(205,44%,22%));
-  color: white !important;
+  color: #f5f7fa;
+  background-image: linear-gradient(#2f607b, #294d68), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.1) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true]:hover {
+  background-image: linear-gradient(#274f64, #224056), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true]:hover:active {
+  background-image: linear-gradient(#1f3e4f, #1b3243), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
 /* Theme */
 
 .devtools-theme-background {
   background-color: white;
 }
 
--- a/browser/themes/pinstripe/devtools/layoutview.css
+++ b/browser/themes/pinstripe/devtools/layoutview.css
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 body {
   background: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
   color: hsl(210,100%,85%);
-  border-top: 1px solid black;
   -moz-box-sizing: border-box;
 }
 
 #element-size {
   color: hsl(210,100%,95%);
 }
 
 #main {
--- a/browser/themes/pinstripe/devtools/splitview.css
+++ b/browser/themes/pinstripe/devtools/splitview.css
@@ -67,17 +67,16 @@
 }
 
 /* Toolbars */
 
 .devtools-toolbar {
   height: 26px;
   background-origin: border-box;
   background-clip: border-box;
-  border-top: 1px solid hsla(210,8%,5%,.5);
   border-bottom: 1px solid hsla(210,8%,5%,.65);
   padding: 3px;
 }
 
 .splitview-main > toolbar:-moz-locale-dir(ltr) {
   border-right: 1px solid hsla(210,8%,5%,.5);
 }
 
--- a/browser/themes/pinstripe/devtools/toolbox.css
+++ b/browser/themes/pinstripe/devtools/toolbox.css
@@ -104,17 +104,19 @@
 
 
 /* Tabs */
 
 .devtools-tabbar {
   -moz-appearance: none;
   background-image: url("background-noise-toolbar.png"),
                     linear-gradient(#303840, #2d3640);
-  border-top: 1px solid #060a0d;
+  border-color: #060a0d;
+  border-style: solid;
+  border-width: 1px 0;
   box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset,
               0 -1px 0 hsla(206,37%,4%,.1) inset;
   min-height: 32px;
   padding: 0;
 }
 
 #toolbox-tabs {
   margin: 0;
--- a/browser/themes/winstripe/devtools/common.css
+++ b/browser/themes/winstripe/devtools/common.css
@@ -10,19 +10,20 @@
   font-family: monospace;
 }
 
 /* Toolbar and Toolbar items */
 
 .devtools-toolbar {
   -moz-appearance: none;
   padding: 4px 3px;
-  box-shadow: 0 1px 0 hsla(209,29%,72%,.25) inset;
-  background-image: -moz-linear-gradient(top, hsl(209,18%,34%), hsl(210,24%,16%));
   color: hsl(210,30%,85%);
+  background-image: url(background-noise-toolbar.png), linear-gradient(#3e4750, #3e4750);
+  border-bottom: 1px solid #060a0d;
+  box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset, -1px 0 0 hsla(204,45%,98%,.05) inset, 0 -1px 0 hsla(204,45%,98%,.05) inset;
 }
 
 .devtools-menulist,
 .devtools-toolbarbutton {
   -moz-appearance: none;
   -moz-box-align: center;
   min-width: 78px;
   min-height: 22px;
@@ -237,57 +238,120 @@
   -moz-appearance: none;
   padding: 0;
   border: 0;
 }
 
 .devtools-sidebar-tabs > tabs {
   -moz-appearance: none;
   position: static;
-  box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
-  background-image: linear-gradient(to bottom, hsl(210,11%,36%), hsl(210,11%,18%));
   color: hsl(210,30%,85%);
   margin-bottom: 0;
   padding: 0;
+  background-image: url(background-noise-toolbar.png), linear-gradient(#3e4750, #3e4750);
+  box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset, -1px 0 0 hsla(204,45%,98%,.05) inset, 0 -1px 0 hsla(204,45%,98%,.05) inset;
+  border-width: 0 0 1px 0;
+  border-color: hsla(210,8%,5%,.6);
+  border-style: solid;
+  overflow: hidden;
 }
 
 .devtools-sidebar-tabs > tabs > .tabs-right,
 .devtools-sidebar-tabs > tabs > .tabs-left {
   display: none;
 }
 
 .devtools-sidebar-tabs > tabs > tab {
   -moz-appearance: none;
-  padding: 0;
+  /* We want to match the height of a toolbar with a toolbarbutton 
+   * First, we need to replicated the padding of toolbar (4px),
+   * then, the padding of the button itself from toolbarbutton.css (3px),
+   * Also, we need to take the border of the buttons into accout (1px).
+   * Padding-bottom is one pixel shorter because we need to include the
+   * black border.
+   */
+  padding: 8px 3px 7px;
+  -moz-padding-start: 6px;
   margin: 0;
   min-width: 78px;
-  min-height: 22px;
   text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
   text-align: center;
   color: inherit;
   -moz-box-flex: 1;
   border-width: 0;
-  -moz-border-end-width: 1px;
-  border-color: hsla(210,8%,5%,.6);
-  border-style: solid;
   background: transparent;
   border-radius: 0;
+  position: static;
+  -moz-margin-start: -1px;
 }
 
 .devtools-sidebar-tabs > tabs > tab:-moz-focusring {
   position: static;
 }
 
 .devtools-sidebar-tabs > tabs > tab:last-of-type {
   -moz-border-end-width: 0;
 }
 
+.devtools-sidebar-tabs > tabs > tab:first-of-type {
+  -moz-margin-start: -3px;
+}
+
+.devtools-sidebar-tabs > tabs > tab {
+  background-size: 100% 100%, 1px 100%, 1px 100%, 1px 100%;
+  background-repeat: no-repeat;
+  background-position: 2px, 0, 1px, 2px;
+}
+
+.devtools-sidebar-tabs:-moz-locale-dir(rtl) > tabs > tab {
+  background-position: calc(100% - 3px), 100%, calc(100% - 1px), calc(100% - 2px);
+}
+
+%filter substitution
+%define smallSeparator linear-gradient(hsla(204,45%,98%,0), hsla(204,45%,98%,.1), hsla(204,45%,98%,0)), linear-gradient(hsla(206,37%,4%,0), hsla(206,37%,4%,.6), hsla(206,37%,4%,0)), linear-gradient(hsla(204,45%,98%,0), hsla(204,45%,98%,.1), hsla(204,45%,98%,0))
+%define solidSeparator linear-gradient(transparent, transparent), linear-gradient(hsla(206,37%,4%,.6), hsla(206,37%,4%,.7)), linear-gradient(hsla(204,45%,98%,.1), hsla(204,45%,98%,.1))
+
+.devtools-sidebar-tabs > tabs > tab {
+  background-image: linear-gradient(transparent, transparent), @smallSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab:hover {
+  background-image: linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.2)), @smallSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab:hover:active {
+  background-image: linear-gradient(hsla(206,37%,4%,.4), hsla(206,37%,4%,.4)), @smallSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab {
+  background-image: linear-gradient(transparent, transparent), @solidSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab:hover {
+  background-image: linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.2)), @solidSeparator@;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true] + tab:hover:active {
+  background-image: linear-gradient(hsla(206,37%,4%,.4), hsla(206,37%,4%,.4)), @solidSeparator@;
+}
+
 .devtools-sidebar-tabs > tabs > tab[selected=true] {
-  background-image: linear-gradient(to bottom, hsl(201,45%,34%), hsl(205,44%,22%));
-  color: white !important;
+  color: #f5f7fa;
+  background-image: linear-gradient(#2f607b, #294d68), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.1) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true]:hover {
+  background-image: linear-gradient(#274f64, #224056), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
+}
+
+.devtools-sidebar-tabs > tabs > tab[selected=true]:hover:active {
+  background-image: linear-gradient(#1f3e4f, #1b3243), @solidSeparator@;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 -2px 0 hsla(206,37%,4%,.05) inset, 0 -1px 1px hsla(206,37%,4%,.1) inset;
 }
 
 /* Theme */
 
 .devtools-theme-background {
   background-color: white;
 }
 
--- a/browser/themes/winstripe/devtools/layoutview.css
+++ b/browser/themes/winstripe/devtools/layoutview.css
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 body {
   background: url(layout-background-grid.png), -moz-radial-gradient(50% 70%, circle cover, hsl(210,53%,45%) 0%, hsl(210,54%,33%) 100%);
   color: hsl(210,100%,85%);
-  border-top: 1px solid black;
   -moz-box-sizing: border-box;
 }
 
 #element-size {
   color: hsl(210,100%,95%);
 }
 
 #main {
--- a/browser/themes/winstripe/devtools/toolbox.css
+++ b/browser/themes/winstripe/devtools/toolbox.css
@@ -119,17 +119,17 @@
 
 
 /* Tabs */
 
 .devtools-tabbar {
   -moz-appearance: none;
   background-image: url("background-noise-toolbar.png"),
                     linear-gradient(#303840, #2d3640);
-  border: none;
+  border-bottom: 1px solid #060a0d;
   box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset,
               0 -1px 0 hsla(206,37%,4%,.1) inset;
   min-height: 32px;
   padding: 0;
 }
 
 #toolbox-tabs {
   margin: 0;
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -129,17 +129,17 @@ browser.jar:
         skin/classic/browser/tabview/close.png                      (tabview/close.png)
         skin/classic/browser/tabview/edit-light.png                 (tabview/edit-light.png)
         skin/classic/browser/tabview/grain.png                      (tabview/grain.png)
         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/common.css                    (devtools/common.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/commandline.css               (devtools/commandline.css)
         skin/classic/browser/devtools/command-responsivemode.png    (devtools/command-responsivemode.png)
         skin/classic/browser/devtools/command-scratchpad.png        (devtools/command-scratchpad.png)
@@ -182,16 +182,17 @@ browser.jar:
         skin/classic/browser/devtools/splitview.css                 (devtools/splitview.css)
         skin/classic/browser/devtools/styleeditor.css               (devtools/styleeditor.css)
         skin/classic/browser/devtools/debugger.css                  (devtools/debugger.css)
         skin/classic/browser/devtools/magnifying-glass.png          (devtools/magnifying-glass.png)
         skin/classic/browser/devtools/option-icon.png               (devtools/option-icon.png)
         skin/classic/browser/devtools/itemToggle.png                (devtools/itemToggle.png)
         skin/classic/browser/devtools/itemArrow-rtl.png             (devtools/itemArrow-rtl.png)
         skin/classic/browser/devtools/itemArrow-ltr.png             (devtools/itemArrow-ltr.png)
+        skin/classic/browser/devtools/background-noise-toolbar.png  (devtools/background-noise-toolbar.png)
         skin/classic/browser/devtools/inspect-button.png            (devtools/inspect-button.png)
         skin/classic/browser/devtools/dropmarker.png                (devtools/dropmarker.png)
         skin/classic/browser/devtools/layout-background-grid.png    (devtools/layout-background-grid.png)
         skin/classic/browser/devtools/layoutview.css                (devtools/layoutview.css)
         skin/classic/browser/devtools/debugger-collapse.png         (devtools/debugger-collapse.png)
         skin/classic/browser/devtools/debugger-expand.png           (devtools/debugger-expand.png)
         skin/classic/browser/devtools/debugger-pause.png            (devtools/debugger-pause.png)
         skin/classic/browser/devtools/debugger-play.png             (devtools/debugger-play.png)
@@ -354,17 +355,17 @@ browser.jar:
         skin/classic/aero/browser/tabview/close.png                  (tabview/close.png)
         skin/classic/aero/browser/tabview/edit-light.png             (tabview/edit-light.png)
         skin/classic/aero/browser/tabview/grain.png                  (tabview/grain.png)
         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/common.css                (devtools/common.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/command-responsivemode.png (devtools/command-responsivemode.png)
         skin/classic/aero/browser/devtools/command-scratchpad.png    (devtools/command-scratchpad.png)
         skin/classic/aero/browser/devtools/command-tilt.png          (devtools/command-tilt.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)
@@ -406,16 +407,17 @@ browser.jar:
         skin/classic/aero/browser/devtools/breadcrumbs/rtl-start-selected.png           (devtools/breadcrumbs/rtl-start-selected.png)
         skin/classic/aero/browser/devtools/splitview.css             (devtools/splitview.css)
         skin/classic/aero/browser/devtools/styleeditor.css           (devtools/styleeditor.css)
         skin/classic/aero/browser/devtools/debugger.css              (devtools/debugger.css)
         skin/classic/aero/browser/devtools/magnifying-glass.png      (devtools/magnifying-glass.png)
         skin/classic/aero/browser/devtools/option-icon.png           (devtools/option-icon.png)
         skin/classic/aero/browser/devtools/itemToggle.png            (devtools/itemToggle.png)
         skin/classic/aero/browser/devtools/itemArrow-rtl.png         (devtools/itemArrow-rtl.png)
+        skin/classic/aero/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
         skin/classic/aero/browser/devtools/itemArrow-ltr.png         (devtools/itemArrow-ltr.png)
         skin/classic/aero/browser/devtools/inspect-button.png        (devtools/inspect-button.png)
         skin/classic/aero/browser/devtools/dropmarker.png            (devtools/dropmarker.png)
         skin/classic/aero/browser/devtools/layout-background-grid.png (devtools/layout-background-grid.png)
         skin/classic/aero/browser/devtools/layoutview.css            (devtools/layoutview.css)
         skin/classic/aero/browser/devtools/debugger-collapse.png     (devtools/debugger-collapse.png)
         skin/classic/aero/browser/devtools/debugger-expand.png       (devtools/debugger-expand.png)
         skin/classic/aero/browser/devtools/debugger-pause.png        (devtools/debugger-pause.png)
--- a/dom/base/ConsoleAPI.js
+++ b/dom/base/ConsoleAPI.js
@@ -250,90 +250,88 @@ ConsoleAPI.prototype = {
    * @private
    * @param array aCall
    *        Array that holds information about the queued call.
    */
   _processQueuedCall: function CA__processQueuedItem(aCall)
   {
     let [method, args, meta] = aCall;
 
-    let notifyMeta = {
-      isPrivate: meta.isPrivate,
+    let frame = meta.stack[0];
+    let consoleEvent = {
+      ID: this._outerID,
+      innerID: this._innerID,
+      level: method,
+      filename: frame.filename,
+      lineNumber: frame.lineNumber,
+      functionName: frame.functionName,
       timeStamp: meta.timeStamp,
-      frame: meta.stack[0],
+      arguments: args,
     };
 
-    let notifyArguments = null;
-
     switch (method) {
       case "log":
       case "info":
       case "warn":
       case "error":
       case "debug":
-        notifyArguments = this.processArguments(args);
+        consoleEvent.arguments = this.processArguments(args);
         break;
       case "trace":
-        notifyArguments = meta.stack;
+        consoleEvent.stacktrace = meta.stack;
         break;
       case "group":
       case "groupCollapsed":
-        notifyArguments = this.beginGroup(args);
+      case "groupEnd":
+        try {
+          consoleEvent.groupName = Array.prototype.join.call(args, " ");
+        }
+        catch (ex) {
+          Cu.reportError(ex);
+          Cu.reportError(ex.stack);
+          return;
+        }
         break;
-      case "groupEnd":
       case "dir":
-        notifyArguments = args;
         break;
       case "time":
-        notifyArguments = this.startTimer(args[0], meta.timeStamp);
+        consoleEvent.timer = this.startTimer(args[0], meta.timeStamp);
         break;
       case "timeEnd":
-        notifyArguments = this.stopTimer(args[0], meta.timeStamp);
+        consoleEvent.timer = this.stopTimer(args[0], meta.timeStamp);
         break;
       default:
         // unknown console API method!
         return;
     }
 
-    this.notifyObservers(method, notifyArguments, notifyMeta);
+    this.notifyObservers(method, consoleEvent, meta.isPrivate);
   },
 
   /**
    * Notify all observers of any console API call.
    *
    * @param string aLevel
    *        The message level.
-   * @param mixed aArguments
-   *        The arguments given to the console API call.
-   * @param object aMeta
-   *        Object that holds metadata about the console API call:
-   *        - isPrivate - Whether the window is in private browsing mode.
-   *        - frame - the youngest content frame in the call stack.
-   *        - timeStamp - when the console API call occurred.
+   * @param object aConsoleEvent
+   *        The console event object to send to observers for the given console
+   *        API call.
+   * @param boolean aPrivate
+   *        Tells whether the window is in private browsing mode.
    */
-  notifyObservers: function CA_notifyObservers(aLevel, aArguments, aMeta) {
-    let consoleEvent = {
-      ID: this._outerID,
-      innerID: this._innerID,
-      level: aLevel,
-      filename: aMeta.frame.filename,
-      lineNumber: aMeta.frame.lineNumber,
-      functionName: aMeta.frame.functionName,
-      arguments: aArguments,
-      timeStamp: aMeta.timeStamp,
-    };
-
-    consoleEvent.wrappedJSObject = consoleEvent;
+  notifyObservers: function CA_notifyObservers(aLevel, aConsoleEvent, aPrivate)
+  {
+    aConsoleEvent.wrappedJSObject = aConsoleEvent;
 
     // Store non-private messages for which the inner window was not destroyed.
-    if (!aMeta.isPrivate) {
-      ConsoleAPIStorage.recordEvent(this._innerID, consoleEvent);
+    if (!aPrivate) {
+      ConsoleAPIStorage.recordEvent(this._innerID, aConsoleEvent);
     }
 
-    Services.obs.notifyObservers(consoleEvent, "console-api-log-event",
+    Services.obs.notifyObservers(aConsoleEvent, "console-api-log-event",
                                  this._outerID);
   },
 
   /**
    * Process the console API call arguments in order to perform printf-like
    * string substitution.
    *
    * TODO: object substitution should take into account width and precision
@@ -413,23 +411,16 @@ ConsoleAPI.prototype = {
           break;
         }
       }
     }
 
     return stack;
   },
 
-  /**
-   * Begin a new group for logging output together.
-   **/
-  beginGroup: function CA_beginGroup() {
-    return Array.prototype.join.call(arguments[0], " ");
-  },
-
   /*
    * A registry of started timers. Timer maps are key-value pairs of timer
    * names to timer start times, for all timers defined in the page. Timer
    * names are prepended with the inner window ID in order to avoid conflicts
    * with Object.prototype functions.
    */
   timerRegistry: null,
 
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -34,23 +34,24 @@ function test() {
 }
 
 function testConsoleData(aMessageObject) {
   let messageWindow = getWindowByWindowId(aMessageObject.ID);
   is(messageWindow, gWindow, "found correct window by window ID");
 
   is(aMessageObject.level, gLevel, "expected level received");
   ok(aMessageObject.arguments, "we have arguments");
-  is(aMessageObject.arguments.length, gArgs.length, "arguments.length matches");
 
   if (gLevel == "trace") {
-    is(aMessageObject.arguments.toSource(), gArgs.toSource(),
+    is(aMessageObject.arguments.length, 0, "arguments.length matches");
+    is(aMessageObject.stacktrace.toSource(), gArgs.toSource(),
        "stack trace is correct");
   }
   else {
+    is(aMessageObject.arguments.length, gArgs.length, "arguments.length matches");
     gArgs.forEach(function (a, i) {
       is(aMessageObject.arguments[i], a, "correct arg " + i);
     });
   }
 
   gTestDriver.next();
 }
 
@@ -96,23 +97,29 @@ function testConsoleGroup(aMessageObject
      aMessageObject.level == "groupCollapsed" ||
      aMessageObject.level == "groupEnd",
      "expected level received");
 
   is(aMessageObject.functionName, "testGroups", "functionName matches");
   ok(aMessageObject.lineNumber >= 45 && aMessageObject.lineNumber <= 47,
      "lineNumber matches");
   if (aMessageObject.level == "groupCollapsed") {
-    ok(aMessageObject.arguments == "a group", "groupCollapsed arguments matches");
+    is(aMessageObject.groupName, "a group", "groupCollapsed groupName matches");
+    is(aMessageObject.arguments[0], "a", "groupCollapsed arguments[0] matches");
+    is(aMessageObject.arguments[1], "group", "groupCollapsed arguments[0] matches");
   }
   else if (aMessageObject.level == "group") {
-    ok(aMessageObject.arguments == "b group", "group arguments matches");
+    is(aMessageObject.groupName, "b group", "group groupName matches");
+    is(aMessageObject.arguments[0], "b", "group arguments[0] matches");
+    is(aMessageObject.arguments[1], "group", "group arguments[1] matches");
   }
   else if (aMessageObject.level == "groupEnd") {
-    ok(Array.prototype.join.call(aMessageObject.arguments, " ") == "b group", "groupEnd arguments matches");
+    let groupName = Array.prototype.join.call(aMessageObject.arguments, " ");
+    is(groupName,"b group", "groupEnd arguments matches");
+    is(aMessageObject.groupName, "b group", "groupEnd groupName matches");
   }
 
   if (aMessageObject.level == "groupEnd") {
     startTimeTest();
   }
 }
 
 function startTraceTest() {
@@ -247,33 +254,42 @@ function startTimeTest() {
     } catch (ex) {
       // XXX Exceptions in this function currently aren't reported, because of
       // some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
   gLevel = "time";
   gArgs = [
-    {filename: TEST_URI, lineNumber: 23, functionName: "startTimer"},
+    {filename: TEST_URI, lineNumber: 23, functionName: "startTimer",
+     arguments: ["foo"],
+     timer: { name: "foo" },
+    }
   ];
 
   let button = gWindow.document.getElementById("test-time");
   ok(button, "found #test-time button");
   EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
 }
 
 function testConsoleTime(aMessageObject) {
   let messageWindow = getWindowByWindowId(aMessageObject.ID);
   is(messageWindow, gWindow, "found correct window by window ID");
 
   is(aMessageObject.level, gLevel, "expected level received");
 
   is(aMessageObject.filename, gArgs[0].filename, "filename matches");
   is(aMessageObject.lineNumber, gArgs[0].lineNumber, "lineNumber matches");
   is(aMessageObject.functionName, gArgs[0].functionName, "functionName matches");
+  is(aMessageObject.timer.name, gArgs[0].timer.name, "timer.name matches");
+  ok(aMessageObject.timer.started, "timer.started exists");
+
+  gArgs[0].arguments.forEach(function (a, i) {
+    is(aMessageObject.arguments[i], a, "correct arg " + i);
+  });
 
   startTimeEndTest();
 }
 
 function startTimeEndTest() {
   // Reset the observer function to cope with the fabricated test data.
   ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
     try {
@@ -281,17 +297,20 @@ function startTimeEndTest() {
     } catch (ex) {
       // XXX Exceptions in this function currently aren't reported, because of
       // some XPConnect weirdness, so report them manually
       ok(false, "Exception thrown in CO_observe: " + ex);
     }
   };
   gLevel = "timeEnd";
   gArgs = [
-    {filename: TEST_URI, lineNumber: 27, functionName: "stopTimer", arguments: { name: "foo" }},
+    {filename: TEST_URI, lineNumber: 27, functionName: "stopTimer",
+     arguments: ["foo"],
+     timer: { name: "foo" },
+    },
   ];
 
   let button = gWindow.document.getElementById("test-timeEnd");
   ok(button, "found #test-timeEnd button");
   EventUtils.synthesizeMouseAtCenter(button, {}, gWindow);
 }
 
 function testConsoleTimeEnd(aMessageObject) {
@@ -300,19 +319,23 @@ function testConsoleTimeEnd(aMessageObje
 
   is(aMessageObject.level, gLevel, "expected level received");
   ok(aMessageObject.arguments, "we have arguments");
 
   is(aMessageObject.filename, gArgs[0].filename, "filename matches");
   is(aMessageObject.lineNumber, gArgs[0].lineNumber, "lineNumber matches");
   is(aMessageObject.functionName, gArgs[0].functionName, "functionName matches");
   is(aMessageObject.arguments.length, gArgs[0].arguments.length, "arguments.length matches");
-  is(aMessageObject.arguments.name, gArgs[0].arguments.name, "timer name matches");
-  ok(typeof aMessageObject.arguments.duration == "number", "timer duration is a number");
-  ok(aMessageObject.arguments.duration > 0, "timer duration is positive");
+  is(aMessageObject.timer.name, gArgs[0].timer.name, "timer name matches");
+  is(typeof aMessageObject.timer.duration, "number", "timer duration is a number");
+  ok(aMessageObject.timer.duration > 0, "timer duration is positive");
+
+  gArgs[0].arguments.forEach(function (a, i) {
+    is(aMessageObject.arguments[i], a, "correct arg " + i);
+  });
 
   startEmptyTimerTest();
 }
 
 function startEmptyTimerTest() {
   // Reset the observer function to cope with the fabricated test data.
   ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
     try {
@@ -330,17 +353,18 @@ function startEmptyTimerTest() {
 }
 
 function testEmptyTimer(aMessageObject) {
   let messageWindow = getWindowByWindowId(aMessageObject.ID);
   is(messageWindow, gWindow, "found correct window by window ID");
 
   ok(aMessageObject.level == "time" || aMessageObject.level == "timeEnd",
      "expected level received");
-  ok(!aMessageObject.arguments, "we don't have arguments");
+  is(aMessageObject.arguments.length, 0, "we don't have arguments");
+  ok(!aMessageObject.timer, "we don't have a timer");
 
   is(aMessageObject.functionName, "namelessTimer", "functionName matches");
   ok(aMessageObject.lineNumber == 31 || aMessageObject.lineNumber == 32,
      "lineNumber matches");
   // Test finished
   ConsoleObserver.destroy();
   finish();
 }
--- a/toolkit/devtools/debugger/dbg-transport.js
+++ b/toolkit/devtools/debugger/dbg-transport.js
@@ -118,17 +118,21 @@ DebuggerTransport.prototype = {
 
   onDataAvailable: function DT_onDataAvailable(aRequest, aContext,
                                                 aStream, aOffset, aCount) {
     try {
       this._incoming += NetUtil.readInputStreamToString(aStream,
                                                         aStream.available());
       while (this._processIncoming()) {};
     } catch(e) {
-      dumpn("Unexpected error reading from debugging connection: " + e + " - " + e.stack);
+      let msg = "Unexpected error reading from debugging connection: " + e + " - " + e.stack;
+      if (Cu.reportError) {
+        Cu.reportError(msg);
+      }
+      dump(msg + "\n");
       this.close();
       return;
     }
   },
 
   /**
    * Process incoming packets. Returns true if a packet has been received, either
    * if it was properly parsed or not. Returns false if the incoming stream does
@@ -153,28 +157,36 @@ DebuggerTransport.prototype = {
     this._incoming = this._incoming.substring(sep + 1);
     let packet = this._incoming.substring(0, count);
     this._incoming = this._incoming.substring(count);
 
     try {
       packet = this._converter.ConvertToUnicode(packet);
       var parsed = JSON.parse(packet);
     } catch(e) {
-      dumpn("Error parsing incoming packet: " + packet + " (" + e + " - " + e.stack + ")");
+      let msg = "Error parsing incoming packet: " + packet + " (" + e + " - " + e.stack + ")";
+      if (Cu.reportError) {
+        Cu.reportError(msg);
+      }
+      dump(msg + "\n");
       return true;
     }
 
     try {
       dumpn("Got: " + packet);
       let self = this;
       Services.tm.currentThread.dispatch({run: function() {
         self.hooks.onPacket(parsed);
       }}, 0);
     } catch(e) {
-      dumpn("Error handling incoming packet: " + e + " - " + e.stack);
+      let msg = "Error handling incoming packet: " + e + " - " + e.stack;
+      if (Cu.reportError) {
+        Cu.reportError(msg);
+      }
+      dump(msg + "\n");
       dumpn("Packet was: " + packet);
     }
 
     return true;
   }
 }
 
 
@@ -207,18 +219,22 @@ LocalDebuggerTransport.prototype = {
         dumpn("Got: " + JSON.stringify(aPacket, null, 2));
       }
       this._deepFreeze(aPacket);
       let self = this;
       Services.tm.currentThread.dispatch({run: function() {
         self.other.hooks.onPacket(aPacket);
       }}, 0);
     } catch(e) {
-      dumpn("Error handling incoming packet: " + e + " - " + e.stack);
-      dumpn("Packet was: " + aPacket);
+      let msg = "Error handling incoming packet: " + e + " - " + e.stack;
+      if (Cu.reportError) {
+        Cu.reportError(msg);
+      }
+      dump(msg + "\n");
+      dumpn("Packet was: " + aPacket + "\n");
     }
   },
 
   /**
    * Close the transport.
    */
   close: function LDT_close() {
     if (this.other) {
--- a/toolkit/devtools/sourcemap/SourceMap.jsm
+++ b/toolkit/devtools/sourcemap/SourceMap.jsm
@@ -10,26 +10,26 @@
  *
  * Do not edit this file directly, it is built from the sources at
  * https://github.com/mozilla/source-map/
  */
 
 ///////////////////////////////////////////////////////////////////////////////
 
 
-this.EXPORTED_SYMBOLS = [ "SourceMapConsumer", "SourceMapGenerator", "SourceNode" ];
+var EXPORTED_SYMBOLS = [ "SourceMapConsumer", "SourceMapGenerator", "SourceNode" ];
 
 Components.utils.import('resource://gre/modules/devtools/Require.jsm');
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
-define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) {
+define('source-map/source-map-consumer', ['require', 'exports', 'module' ,  'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) {
 
   var util = require('source-map/util');
   var binarySearch = require('source-map/binary-search');
   var ArraySet = require('source-map/array-set').ArraySet;
   var base64VLQ = require('source-map/base64-vlq');
 
   /**
    * A SourceMapConsumer instance represents a parsed source map which we can
@@ -74,16 +74,18 @@ define('source-map/source-map-consumer',
     var file = util.getArg(sourceMap, 'file');
 
     if (version !== this._version) {
       throw new Error('Unsupported version: ' + version);
     }
 
     this._names = ArraySet.fromArray(names);
     this._sources = ArraySet.fromArray(sources);
+    this._sourceRoot = sourceRoot;
+    this.file = file;
 
     // `this._generatedMappings` and `this._originalMappings` hold the parsed
     // mapping coordinates from the source map's "mappings" attribute. Each
     // object in the array is of the form
     //
     //     {
     //       generatedLine: The line number in the generated code,
     //       generatedColumn: The column number in the generated code,
@@ -109,16 +111,27 @@ define('source-map/source-map-consumer',
   }
 
   /**
    * The version of the source mapping spec that we are consuming.
    */
   SourceMapConsumer.prototype._version = 3;
 
   /**
+   * The list of original sources.
+   */
+  Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
+    get: function () {
+      return this._sources.toArray().map(function (s) {
+        return this._sourceRoot ? util.join(this._sourceRoot, s) : s;
+      }, this);
+    }
+  });
+
+  /**
    * Parse the mappings in a string in to a data structure which we can easily
    * query (an ordered list in this._generatedMappings).
    */
   SourceMapConsumer.prototype._parseMappings =
     function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
       var generatedLine = 1;
       var previousGeneratedColumn = 0;
       var previousOriginalLine = 0;
@@ -333,26 +346,66 @@ define('source-map/source-map-consumer',
       }
 
       return {
         line: null,
         column: null
       };
     };
 
+  SourceMapConsumer.GENERATED_ORDER = 1;
+  SourceMapConsumer.ORIGINAL_ORDER = 2;
+
+  /**
+   * Iterate over each mapping between an original source/line/column and a
+   * generated line/column in this source map.
+   *
+   * @param Function aCallback
+   *        The function that is called with each mapping. This function should
+   *        not mutate the mapping.
+   * @param Object aContext
+   *        Optional. If specified, this object will be the value of `this` every
+   *        time that `aCallback` is called.
+   * @param aOrder
+   *        Either `SourceMapConsumer.GENERATED_ORDER` or
+   *        `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
+   *        iterate over the mappings sorted by the generated file's line/column
+   *        order or the original's source/line/column order, respectively. Defaults to
+   *        `SourceMapConsumer.GENERATED_ORDER`.
+   */
+  SourceMapConsumer.prototype.eachMapping =
+    function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
+      var context = aContext || null;
+      var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
+
+      var mappings;
+      switch (order) {
+      case SourceMapConsumer.GENERATED_ORDER:
+        mappings = this._generatedMappings;
+        break;
+      case SourceMapConsumer.ORIGINAL_ORDER:
+        mappings = this._originalMappings;
+        break;
+      default:
+        throw new Error("Unknown order of iteration.");
+      }
+
+      mappings.forEach(aCallback, context);
+    };
+
   exports.SourceMapConsumer = SourceMapConsumer;
 
 });
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
-define('source-map/util', ['require', 'exports', 'module' ], function(require, exports, module) {
+define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) {
 
   /**
    * This is a helper function for getting values from parameter/options
    * objects.
    *
    * @param args The object we are extracting values from
    * @param name The name of the property we are getting.
    * @param defaultValue An optional value to return if the property is missing
@@ -379,17 +432,17 @@ define('source-map/util', ['require', 'e
 
 });
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
-define('source-map/binary-search', ['require', 'exports', 'module' ], function(require, exports, module) {
+define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) {
 
   /**
    * Recursive implementation of binary search.
    *
    * @param aLow Indices here and lower do not contain the needle.
    * @param aHigh Indices here and higher do not contain the needle.
    * @param aNeedle The element being searched for.
    * @param aHaystack The non-empty array being searched.
@@ -457,17 +510,17 @@ define('source-map/binary-search', ['req
 
 });
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
-define('source-map/array-set', ['require', 'exports', 'module' ], function(require, exports, module) {
+define('source-map/array-set', ['require', 'exports', 'module' , ], function(require, exports, module) {
 
   /**
    * A data structure which is a combination of an array and a set. Adding a new
    * member is O(1), testing for membership is O(1), and finding the index of an
    * element is O(1). Removing elements from the set is not supported. Only
    * strings are supported for membership.
    */
   function ArraySet() {
@@ -482,55 +535,69 @@ define('source-map/array-set', ['require
     var set = new ArraySet();
     for (var i = 0, len = aArray.length; i < len; i++) {
       set.add(aArray[i]);
     }
     return set;
   };
 
   /**
+   * Because behavior goes wacky when you set `__proto__` on `this._set`, we
+   * have to prefix all the strings in our set with an arbitrary character.
+   *
+   * See https://github.com/mozilla/source-map/pull/31 and
+   * https://github.com/mozilla/source-map/issues/30
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype._toSetString = function ArraySet__toSetString (aStr) {
+    return "$" + aStr;
+  };
+
+  /**
    * Add the given string to this set.
    *
-   * @param String str
+   * @param String aStr
    */
   ArraySet.prototype.add = function ArraySet_add(aStr) {
     if (this.has(aStr)) {
       // Already a member; nothing to do.
       return;
     }
     var idx = this._array.length;
     this._array.push(aStr);
-    this._set[aStr] = idx;
+    this._set[this._toSetString(aStr)] = idx;
   };
 
   /**
    * Is the given string a member of this set?
    *
-   * @param String str
+   * @param String aStr
    */
   ArraySet.prototype.has = function ArraySet_has(aStr) {
-    return Object.prototype.hasOwnProperty.call(this._set, aStr);
+    return Object.prototype.hasOwnProperty.call(this._set,
+                                                this._toSetString(aStr));
   };
 
   /**
    * What is the index of the given string in the array?
    *
-   * @param String str
+   * @param String aStr
    */
   ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
     if (this.has(aStr)) {
-      return this._set[aStr];
+      return this._set[this._toSetString(aStr)];
     }
     throw new Error('"' + aStr + '" is not in the set.');
   };
 
   /**
    * What is the element at the given index?
    *
-   * @param Number idx
+   * @param Number aIdx
    */
   ArraySet.prototype.at = function ArraySet_at(aIdx) {
     if (aIdx >= 0 && aIdx < this._array.length) {
       return this._array[aIdx];
     }
     throw new Error('No element indexed by ' + aIdx);
   };
 
@@ -577,17 +644,17 @@ define('source-map/array-set', ['require
  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) {
+define('source-map/base64-vlq', ['require', 'exports', 'module' ,  'source-map/base64'], function(require, exports, module) {
 
   var base64 = require('source-map/base64');
 
   // A single base 64 digit can contain 6 bits of data. For the base 64 variable
   // length quantities we use in the source map spec, the first bit is the sign,
   // the next four bits are the actual value, and the 6th bit is the
   // continuation bit. The continuation bit tells us whether there are more
   // digits in this value following this digit.
@@ -688,17 +755,17 @@ define('source-map/base64-vlq', ['requir
 
 });
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
-define('source-map/base64', ['require', 'exports', 'module' ], function(require, exports, module) {
+define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) {
 
   var charToIntMap = {};
   var intToCharMap = {};
 
   'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
     .split('')
     .forEach(function (ch, index) {
       charToIntMap[ch] = index;
@@ -727,17 +794,17 @@ define('source-map/base64', ['require', 
 
 });
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
-define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) {
+define('source-map/source-map-generator', ['require', 'exports', 'module' ,  'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) {
 
   var base64VLQ = require('source-map/base64-vlq');
   var util = require('source-map/util');
   var ArraySet = require('source-map/array-set').ArraySet;
 
   /**
    * An instance of the SourceMapGenerator represents a source map which is
    * being built incrementally. To create a new one, you must pass an object
@@ -892,43 +959,51 @@ define('source-map/source-map-generator'
           }
         }
       }
 
       return result;
     };
 
   /**
-   * Render the source map being generated to a string.
+   * Externalize the source map.
    */
-  SourceMapGenerator.prototype.toString =
-    function SourceMapGenerator_toString() {
+  SourceMapGenerator.prototype.toJSON =
+    function SourceMapGenerator_toJSON() {
       var map = {
         version: this._version,
         file: this._file,
         sources: this._sources.toArray(),
         names: this._names.toArray(),
         mappings: this._serializeMappings()
       };
       if (this._sourceRoot) {
         map.sourceRoot = this._sourceRoot;
       }
-      return JSON.stringify(map);
+      return map;
+    };
+
+  /**
+   * Render the source map being generated to a string.
+   */
+  SourceMapGenerator.prototype.toString =
+    function SourceMapGenerator_toString() {
+      return JSON.stringify(this);
     };
 
   exports.SourceMapGenerator = SourceMapGenerator;
 
 });
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
-define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator'], function(require, exports, module) {
+define('source-map/source-node', ['require', 'exports', 'module' ,  'source-map/source-map-generator'], function(require, exports, module) {
 
   var SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
 
   /**
    * SourceNodes provide a way to abstract over interpolating/concatenating
    * snippets of generated JavaScript source code while maintaining the line and
    * column information associated with the original source code.
    *
--- a/toolkit/devtools/sourcemap/tests/unit/Utils.jsm
+++ b/toolkit/devtools/sourcemap/tests/unit/Utils.jsm
@@ -10,17 +10,17 @@
  *
  * Do not edit this file directly, it is built from the sources at
  * https://github.com/mozilla/source-map/
  */
 
 Components.utils.import('resource://gre/modules/devtools/Require.jsm');
 Components.utils.import('resource://gre/modules/devtools/SourceMap.jsm');
 
-this.EXPORTED_SYMBOLS = [ "define", "runSourceMapTests" ];
+let EXPORTED_SYMBOLS = [ "define", "runSourceMapTests" ];
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 define('test/source-map/assert', ['exports'], function (exports) {
 
@@ -73,17 +73,17 @@ define('test/source-map/assert', ['expor
 
 });
 /* -*- Mode: js; js-indent-level: 2; -*- */
 /*
  * Copyright 2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
-define('test/source-map/util', ['require', 'exports', 'module' ], function(require, exports, module) {
+define('test/source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) {
 
   // This is a test mapping which maps functions from two different files
   // (one.js and two.js) to a minified generated source.
   //
   // Here is one.js:
   //
   //   ONE.foo = function (bar) {
   //     return baz(bar);
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_api.js
@@ -0,0 +1,34 @@
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://test/Utils.jsm');
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2012 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define("test/source-map/test-api", ["require", "exports", "module"], function (require, exports, module) {
+
+  var sourceMap;
+  try {
+    sourceMap = require('source-map');
+  } catch (e) {
+    sourceMap = {};
+    Components.utils.import('resource:///modules/devtools/SourceMap.jsm', sourceMap);
+  }
+
+  exports['test that the api is properly exposed in the top level'] = function (assert, util) {
+    assert.equal(typeof sourceMap.SourceMapGenerator, "function");
+    assert.equal(typeof sourceMap.SourceMapConsumer, "function");
+    assert.equal(typeof sourceMap.SourceNode, "function");
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-api', do_throw);
+}
--- a/toolkit/devtools/sourcemap/tests/unit/test_array_set.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_array_set.js
@@ -60,12 +60,20 @@ define("test/source-map/test-array-set",
     assert.strictEqual(set.indexOf('quux'), 3);
 
     assert.strictEqual(set.at(0), 'foo');
     assert.strictEqual(set.at(1), 'bar');
     assert.strictEqual(set.at(2), 'baz');
     assert.strictEqual(set.at(3), 'quux');
   };
 
+  exports['test that you can add __proto__; see github issue #30'] = function (assert, util) {
+    var set = new ArraySet();
+    set.add('__proto__');
+    assert.ok(set.has('__proto__'));
+    assert.strictEqual(set.at(0), '__proto__');
+    assert.strictEqual(set.indexOf('__proto__'), 0);
+  };
+
 });
 function run_test() {
   runSourceMapTests('test/source-map/test-array-set', do_throw);
 }
--- a/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
@@ -20,16 +20,25 @@ define("test/source-map/test-source-map-
     assert.doesNotThrow(function () {
       var map = new SourceMapConsumer(util.testMap);
     });
     assert.doesNotThrow(function () {
       var map = new SourceMapConsumer(JSON.stringify(util.testMap));
     });
   };
 
+  exports['test that the `sources` field has the original sources'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var sources = map.sources;
+
+    assert.equal(sources[0], '/the/root/one.js');
+    assert.equal(sources[1], '/the/root/two.js');
+    assert.equal(sources.length, 2);
+  };
+
   exports['test that the source root is reflected in a mapping\'s source field'] = function (assert, util) {
     var map = new SourceMapConsumer(util.testMap);
     var mapping;
 
     mapping = map.originalPositionFor({
       line: 2,
       column: 1
     });
@@ -76,12 +85,66 @@ define("test/source-map/test-source-map-
   };
 
   exports['test creating source map consumers with )]}\' prefix'] = function (assert, util) {
     assert.doesNotThrow(function () {
       var map = new SourceMapConsumer(")]}'" + JSON.stringify(util.testMap));
     });
   };
 
+  exports['test eachMapping'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var previousLine = -Infinity;
+    var previousColumn = -Infinity;
+    map.eachMapping(function (mapping) {
+      assert.ok(mapping.generatedLine >= previousLine);
+
+      if (mapping.generatedLine === previousLine) {
+        assert.ok(mapping.generatedColumn >= previousColumn);
+        previousColumn = mapping.generatedColumn;
+      }
+      else {
+        previousLine = mapping.generatedLine;
+        previousColumn = -Infinity;
+      }
+    });
+  };
+
+  exports['test iterating over mappings in a different order'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var previousLine = -Infinity;
+    var previousColumn = -Infinity;
+    var previousSource = "";
+    map.eachMapping(function (mapping) {
+      assert.ok(mapping.source >= previousSource);
+
+      if (mapping.source === previousSource) {
+        assert.ok(mapping.originalLine >= previousLine);
+
+        if (mapping.originalLine === previousLine) {
+          assert.ok(mapping.originalColumn >= previousColumn);
+          previousColumn = mapping.originalColumn;
+        }
+        else {
+          previousLine = mapping.originalLine;
+          previousColumn = -Infinity;
+        }
+      }
+      else {
+        previousSource = mapping.source;
+        previousLine = -Infinity;
+        previousColumn = -Infinity;
+      }
+    }, null, SourceMapConsumer.ORIGINAL_ORDER);
+  };
+
+  exports['test that we can set the context for `this` in eachMapping'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var context = {};
+    map.eachMapping(function () {
+      assert.equal(this, context);
+    }, context);
+  };
+
 });
 function run_test() {
   runSourceMapTests('test/source-map/test-source-map-consumer', do_throw);
 }
--- a/toolkit/devtools/sourcemap/tests/unit/test_source_map_generator.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_map_generator.js
@@ -19,16 +19,24 @@ define("test/source-map/test-source-map-
   exports['test some simple stuff'] = function (assert, util) {
     var map = new SourceMapGenerator({
       file: 'foo.js',
       sourceRoot: '.'
     });
     assert.ok(true);
   };
 
+  exports['test JSON serialization'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'foo.js',
+      sourceRoot: '.'
+    });
+    assert.equal(map.toString(), JSON.stringify(map));
+  };
+
   exports['test adding mappings (case 1)'] = function (assert, util) {
     var map = new SourceMapGenerator({
       file: 'generated-foo.js',
       sourceRoot: '.'
     });
 
     assert.doesNotThrow(function () {
       map.addMapping({
--- a/toolkit/devtools/sourcemap/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/sourcemap/tests/unit/xpcshell.ini
@@ -5,8 +5,9 @@ tail =
 [test_source_node.js]
 [test_source_map_generator.js]
 [test_source_map_consumer.js]
 [test_dog_fooding.js]
 [test_binary_search.js]
 [test_base64_vlq.js]
 [test_base64.js]
 [test_array_set.js]
+[test_api.js]
\ No newline at end of file
--- a/toolkit/devtools/webconsole/dbg-webconsole-actors.js
+++ b/toolkit/devtools/webconsole/dbg-webconsole-actors.js
@@ -716,47 +716,31 @@ WebConsoleActor.prototype =
    * @param object aMessage
    *        The original message received from console-api-log-event.
    * @return object
    *         The object that can be sent to the remote client.
    */
   prepareConsoleMessageForRemote:
   function WCA_prepareConsoleMessageForRemote(aMessage)
   {
-    let result = {
-      level: aMessage.level,
-      filename: aMessage.filename,
-      lineNumber: aMessage.lineNumber,
-      functionName: aMessage.functionName,
-      timeStamp: aMessage.timeStamp,
-    };
+    let result = WebConsoleUtils.cloneObject(aMessage);
+    delete result.wrappedJSObject;
+
+    result.arguments = Array.map(aMessage.arguments || [],
+      function(aObj) {
+        return this.createValueGrip(aObj);
+      }, this);
 
-    switch (result.level) {
-      case "trace":
-      case "group":
-      case "groupCollapsed":
-      case "time":
-      case "timeEnd":
-        result.arguments = aMessage.arguments;
-        break;
-      default:
-        result.arguments = Array.map(aMessage.arguments || [],
-          function(aObj) {
-            return this.createValueGrip(aObj);
-          }, this);
-
-        if (result.level == "dir") {
-          result.objectProperties = [];
-          let first = result.arguments[0];
-          if (typeof first == "object" && first && first.inspectable) {
-            let actor = this.getActorByID(first.actor);
-            result.objectProperties = actor.onInspectProperties().properties;
-          }
-        }
-        break;
+    if (result.level == "dir") {
+      result.objectProperties = [];
+      let first = result.arguments[0];
+      if (typeof first == "object" && first && first.inspectable) {
+        let actor = this.getActorByID(first.actor);
+        result.objectProperties = actor.onInspectProperties().properties;
+      }
     }
 
     return result;
   },
 
   /**
    * Find the XUL window that owns the content window.
    *
--- a/toolkit/devtools/webconsole/test/test_consoleapi.html
+++ b/toolkit/devtools/webconsole/test/test_consoleapi.html
@@ -57,17 +57,17 @@ function doConsoleCalls(aState)
       timeStamp: /^\d+$/,
       arguments: [{ type: "null" }],
     },
     {
       level: "trace",
       filename: /test_consoleapi/,
       functionName: "doConsoleCalls",
       timeStamp: /^\d+$/,
-      arguments: [
+      stacktrace: [
         {
           filename: /test_consoleapi/,
           functionName: "doConsoleCalls",
         },
         {
           filename: /test_consoleapi/,
           functionName: "onAttach",
         },