merge m-c to fx-team
authorTim Taubert <ttaubert@mozilla.com>
Sat, 21 Jul 2012 12:50:03 +0200
changeset 100034 61835756b1cd2276a84fbaf04ef218b7c47bd407
parent 100002 045c11dd41a6720d3040545f357ac3f4f53a6dbf (current diff)
parent 100033 b658834ae434835b1f249a8aec8d91f85c08285d (diff)
child 100035 446b788ab99d048922bbd432bca73a9198b4e8b6
child 100059 a42fbbc49fbb0ae2a5c8fd06707086dad041467f
push id12307
push userryanvm@gmail.com
push dateSat, 21 Jul 2012 18:32:33 +0000
treeherdermozilla-inbound@defbe00ca091 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone17.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
merge m-c to fx-team
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -137,78 +137,29 @@
                       label="&printSetupCmd.label;"
                       command="cmd_pageSetup"/>
           </menupopup>
       </splitmenu>
       <menuseparator class="appmenu-menuseparator"/>
       <menu id="appmenu_webDeveloper"
             label="&appMenuWebDeveloper.label;">
         <menupopup id="appmenu_webDeveloper_popup">
-          <menuitem id="appmenu_devToolbar"
-                    type="checkbox"
-                    autocheck="false"
-                    hidden="true"
-                    label="&devToolbarMenu.label;"
-                    command="Tools:DevToolbar"
-                    key="key_devToolbar"/>
-          <menuitem id="appmenu_webConsole"
-                    label="&webConsoleCmd.label;"
-                    type="checkbox"
-                    command="Tools:WebConsole"
-                    key="key_webConsole"/>
-          <menuitem id="appmenu_pageInspect"
-                    hidden="true"
-                    label="&inspectMenu.label;"
-                    type="checkbox"
-                    command="Tools:Inspect"
-                    key="key_inspect"/>
-          <menuitem id="appmenu_responsiveUI"
-                    hidden="true"
-                    label="&responsiveDesignTool.label;"
-                    type="checkbox"
-                    command="Tools:ResponsiveUI"
-                    key="key_responsiveUI"/>
-          <menuitem id="appmenu_debugger"
-                    hidden="true"
-                    type="checkbox"
-                    label="&debuggerMenu.label2;"
-                    key="key_debugger"
-                    command="Tools:Debugger"/>
-          <menuitem id="appmenu_remoteDebugger"
-                    hidden="true"
-                    label="&remoteDebuggerMenu.label;"
-                    command="Tools:RemoteDebugger"/>
-          <menuitem id="appmenu_chromeDebugger"
-                    hidden="true"
-                    label="&chromeDebuggerMenu.label;"
-                    command="Tools:ChromeDebugger"/>
-          <menuitem id="appmenu_scratchpad"
-                    hidden="true"
-                    label="&scratchpad.label;"
-                    key="key_scratchpad"
-                    command="Tools:Scratchpad"/>
-          <menuitem id="appmenu_styleeditor"
-                    hidden="true"
-                    type="checkbox"
-                    label="&styleeditor.label;"
-                    key="key_styleeditor"
-                    command="Tools:StyleEditor"/>
-          <menuitem id="appmenu_pageSource"
-                    label="&viewPageSourceCmd.label;"
-                    command="View:PageSource"
-                    key="key_viewSource"/>
-          <menuitem id="appmenu_errorConsole"
-                    hidden="true"
-                    label="&errorConsoleCmd.label;"
-                    key="key_errorConsole"
-                    oncommand="toJavaScriptConsole();"/>
+          <menuitem id="appmenu_devToolbar" observes="devtoolsMenuBroadcaster_DevToolbar"/>
+          <menuitem id="appmenu_webConsole" observes="devtoolsMenuBroadcaster_WebConsole"/>
+          <menuitem id="appmenu_pageinspect" observes="devtoolsMenuBroadcaster_Inspect"/>
+          <menuitem id="appmenu_responsiveUI" observes="devtoolsMenuBroadcaster_ResponsiveUI"/>
+          <menuitem id="appmenu_debugger" observes="devtoolsMenuBroadcaster_Debugger"/>
+          <menuitem id="appmenu_remoteDebugger" observes="devtoolsMenuBroadcaster_RemoteDebugger"/>
+          <menuitem id="appmenu_chromeDebugger" observes="devtoolsMenuBroadcaster_ChromeDebugger"/>
+          <menuitem id="appmenu_scratchpad" observes="devtoolsMenuBroadcaster_Scratchpad"/>
+          <menuitem id="appmenu_styleeditor" observes="devtoolsMenuBroadcaster_StyleEditor"/>
+          <menuitem id="appmenu_pageSource" observes="devtoolsMenuBroadcaster_PageSource"/>
+          <menuitem id="appmenu_errorConsole" observes="devtoolsMenuBroadcaster_ErrorConsole"/>
           <menuseparator id="appmenu_devToolsEndSeparator"/>
-          <menuitem id="appmenu_getMoreDevtools"
-                    label="&getMoreDevtoolsCmd.label;"
-                    oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/>
+          <menuitem id="appmenu_getMoreDevtools" observes="devtoolsMenuBroadcaster_GetMoreTools"/>
           <menuseparator/>
 #define ID_PREFIX appmenu_developer_
 #define OMIT_ACCESSKEYS
 #include browser-charsetmenu.inc
 #undef ID_PREFIX
 #undef OMIT_ACCESSKEYS
           <menuitem label="&goOfflineCmd.label;"
                     type="checkbox"
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -509,87 +509,29 @@
                         observes="sync-syncnow-state"
                         oncommand="gSyncUI.doSync(event);"/>
 #endif
               <menuseparator id="devToolsSeparator"/>
               <menu id="webDeveloperMenu"
                     label="&webDeveloperMenu.label;"
                     accesskey="&webDeveloperMenu.accesskey;">
                 <menupopup id="menuWebDeveloperPopup">
-                  <menuitem id="menu_devToolbar"
-                            type="checkbox"
-                            autocheck="false"
-                            hidden="true"
-                            label="&devToolbarMenu.label;"
-                            accesskey="&devToolbarMenu.accesskey;"
-                            key="key_devToolbar"
-                            command="Tools:DevToolbar"/>
-                  <menuitem id="webConsole"
-                            type="checkbox"
-                            label="&webConsoleCmd.label;"
-                            accesskey="&webConsoleCmd.accesskey;"
-                            key="key_webConsole"
-                            command="Tools:WebConsole"/>
-                  <menuitem id="menu_pageinspect"
-                            type="checkbox"
-                            hidden="true"
-                            label="&inspectMenu.label;"
-                            accesskey="&inspectMenu.accesskey;"
-                            key="key_inspect"
-                            command="Tools:Inspect"/>
-                  <menuitem id="menu_responsiveUI"
-                            type="checkbox"
-                            hidden="true"
-                            label="&responsiveDesignTool.label;"
-                            accesskey="&responsiveDesignTool.accesskey;"
-                            key="key_responsiveUI"
-                            command="Tools:ResponsiveUI"/>
-                  <menuitem id="menu_debugger"
-                            hidden="true"
-                            type="checkbox"
-                            label="&debuggerMenu.label2;"
-                            key="key_debugger"
-                            command="Tools:Debugger"/>
-                  <menuitem id="menu_remoteDebugger"
-                            hidden="true"
-                            label="&remoteDebuggerMenu.label;"
-                            command="Tools:RemoteDebugger"/>
-                  <menuitem id="menu_chromeDebugger"
-                            hidden="true"
-                            label="&chromeDebuggerMenu.label;"
-                            command="Tools:ChromeDebugger"/>
-                  <menuitem id="menu_scratchpad"
-                            hidden="true"
-                            label="&scratchpad.label;"
-                            accesskey="&scratchpad.accesskey;"
-                            key="key_scratchpad"
-                            command="Tools:Scratchpad"/>
-                  <menuitem id="menu_styleeditor"
-                            type="checkbox"
-                            hidden="true"
-                            label="&styleeditor.label;"
-                            accesskey="&styleeditor.accesskey;"
-                            key="key_styleeditor"
-                            command="Tools:StyleEditor"/>
-                  <menuitem id="menu_pageSource"
-                            accesskey="&pageSourceCmd.accesskey;"
-                            label="&pageSourceCmd.label;"
-                            key="key_viewSource"
-                            command="View:PageSource"/>
-                  <menuitem id="javascriptConsole"
-                            hidden="true"
-                            label="&errorConsoleCmd.label;"
-                            accesskey="&errorConsoleCmd.accesskey;"
-                            key="key_errorConsole"
-                            oncommand="toJavaScriptConsole();"/>
+                  <menuitem id="menu_devToolbar" observes="devtoolsMenuBroadcaster_DevToolbar" accesskey="&devToolbarMenu.accesskey;"/>
+                  <menuitem id="webConsole" observes="devtoolsMenuBroadcaster_WebConsole" accesskey="&webConsoleCmd.accesskey;"/>
+                  <menuitem id="menu_pageinspect" observes="devtoolsMenuBroadcaster_Inspect" accesskey="&inspectMenu.accesskey;"/>
+                  <menuitem id="menu_responsiveUI" observes="devtoolsMenuBroadcaster_ResponsiveUI" accesskey="&responsiveDesignTool.accesskey;"/>
+                  <menuitem id="menu_debugger" observes="devtoolsMenuBroadcaster_Debugger"/>
+                  <menuitem id="menu_remoteDebugger" observes="devtoolsMenuBroadcaster_RemoteDebugger"/>
+                  <menuitem id="menu_chromeDebugger" observes="devtoolsMenuBroadcaster_ChromeDebugger"/>
+                  <menuitem id="menu_scratchpad" observes="devtoolsMenuBroadcaster_Scratchpad" accesskey="&scratchpad.accesskey;"/>
+                  <menuitem id="menu_styleeditor" observes="devtoolsMenuBroadcaster_StyleEditor" accesskey="&styleeditor.accesskey;"/>
+                  <menuitem id="menu_pageSource" observes="devtoolsMenuBroadcaster_PageSource" accesskey="&pageSourceCmd.accesskey;"/>
+                  <menuitem id="javascriptConsole" observes="devtoolsMenuBroadcaster_ErrorConsole" accesskey="&errorConsoleCmd.accesskey;"/>
                   <menuseparator id="devToolsEndSeparator"/>
-                  <menuitem id="getMoreDevtools"
-                            label="&getMoreDevtoolsCmd.label;"
-                            accesskey="&getMoreDevtoolsCmd.accesskey;"
-                            oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/>
+                  <menuitem id="getMoreDevtools" observes="devtoolsMenuBroadcaster_GetMoreTools" accesskey="&getMoreDevtoolsCmd.accesskey;"/>
                 </menupopup>
               </menu>
               <menuitem id="menu_pageInfo"
                         accesskey="&pageInfoCmd.accesskey;"
                         label="&pageInfoCmd.label;"
 #ifndef XP_WIN
                         key="key_viewInfo"
 #endif
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -83,26 +83,26 @@
     <command id="cmd_fullZoomReduce"  oncommand="FullZoom.reduce()"/>
     <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
     <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="Browser:OpenLocation" oncommand="openLocation();"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
-    <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true"/>
+    <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();"/>
     <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focus();" disabled="true"/>
     <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
-    <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
-    <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true"/>
-    <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true"/>
-    <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true"/>
-    <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
-    <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true"/>
-    <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true"/>
+    <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
+    <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();"/>
+    <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();"/>
+    <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();"/>
+    <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();"/>
+    <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();"/>
+    <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
     <command id="Social:SharePage" oncommand="SocialShareButton.sharePage();"/>
@@ -178,16 +178,78 @@
     <broadcaster id="multipleFeedsMenuState" hidden="true"/>
     <broadcaster id="tabviewGroupsNumber" groups="1"/>
 #ifdef MOZ_SERVICES_SYNC
     <broadcaster id="sync-setup-state"/>
     <broadcaster id="sync-syncnow-state"/>
 #endif
     <broadcaster id="workOfflineMenuitemState"/>
     <broadcaster id="socialSidebarBroadcaster" hidden="true"/>
+
+    <!-- DevTools broadcasters -->
+    <broadcaster id="devtoolsMenuBroadcaster_DevToolbar"
+                 label="&devToolbarMenu.label;"
+                 type="checkbox" autocheck="false"
+                 command="Tools:DevToolbar"
+                 key="key_devToolbar"
+                 disabled="true" hidden="true"/>
+    <broadcaster id="devtoolsMenuBroadcaster_WebConsole"
+                 label="&webConsoleCmd.label;"
+                 type="checkbox" autocheck="false"
+                 key="key_webConsole"
+                 command="Tools:WebConsole"/>
+    <broadcaster id="devtoolsMenuBroadcaster_Inspect"
+                 label="&inspectMenu.label;"
+                 type="checkbox" autocheck="false"
+                 command="Tools:Inspect"
+                 key="key_inspect"
+                 disabled="true" hidden="true"/>
+    <broadcaster id="devtoolsMenuBroadcaster_Debugger"
+                 label="&debuggerMenu.label2;"
+                 type="checkbox" autocheck="false"
+                 command="Tools:Debugger"
+                 key="key_debugger"
+                 disabled="true" hidden="true"/>
+    <broadcaster id="devtoolsMenuBroadcaster_RemoteDebugger"
+                 label="&remoteDebuggerMenu.label;"
+                 command="Tools:RemoteDebugger"
+                 disabled="true" hidden="true"/>
+    <broadcaster id="devtoolsMenuBroadcaster_ChromeDebugger"
+                 label="&chromeDebuggerMenu.label;"
+                 command="Tools:ChromeDebugger"
+                 disabled="true" hidden="true"/>
+    <broadcaster id="devtoolsMenuBroadcaster_Scratchpad"
+                 label="&scratchpad.label;"
+                 command="Tools:Scratchpad"
+                 key="key_scratchpad"
+                 disabled="true" hidden="true"/>
+    <broadcaster id="devtoolsMenuBroadcaster_StyleEditor"
+                 label="&styleeditor.label;"
+                 type="checkbox" autocheck="false"
+                 command="Tools:StyleEditor"
+                 key="key_styleeditor"
+                 disabled="true" hidden="true"/>
+    <broadcaster id="devtoolsMenuBroadcaster_ResponsiveUI"
+                 label="&responsiveDesignTool.label;"
+                 type="checkbox" autocheck="false"
+                 command="Tools:ResponsiveUI"
+                 key="key_responsiveUI"
+                 disabled="true" hidden="true"/>
+    <broadcaster id="devtoolsMenuBroadcaster_PageSource"
+                 label="&pageSourceCmd.label;"
+                 key="key_viewSource"
+                 command="View:PageSource"/>
+    <broadcaster id="devtoolsMenuBroadcaster_ErrorConsole"
+                 hidden="true"
+                 label="&errorConsoleCmd.label;"
+                 key="key_errorConsole"
+                 oncommand="toJavaScriptConsole();"/>
+    <broadcaster id="devtoolsMenuBroadcaster_GetMoreTools"
+                 label="&getMoreDevtoolsCmd.label;"
+                 oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/>
   </broadcasterset>
 
   <keyset id="mainKeyset">
     <key id="key_newNavigator"
          key="&newNavigatorCmd.key;"
          command="cmd_newNavigator"
          modifiers="accel"/>
     <key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel" command="cmd_newNavigatorTab"/>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -573,16 +573,22 @@ html|*#gcli-output-frame,
 }
 
 #developer-toolbar-webconsole[error-count]:before {
   content: attr(error-count);
   display: -moz-box;
   -moz-box-pack: center;
 }
 
+/* We don't show the Style Editor button in the developer toolbar for now.
+   See bug 771203 */
+#developer-toolbar-styleeditor {
+  display: none;
+}
+
 /* Responsive Mode */
 
 vbox[anonid=browserContainer][responsivemode] {
   overflow: auto;
 }
 
 .devtools-responsiveui-toolbar:-moz-locale-dir(rtl) {
   -moz-box-pack: end;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1397,124 +1397,101 @@ var gBrowserInit = {
     window.addEventListener("resize", function resizeHandler(event) {
       if (event.target == window)
         setUrlAndSearchBarWidthForConditionalForwardButton();
     });
 
     // Enable developer toolbar?
     let devToolbarEnabled = gPrefService.getBoolPref("devtools.toolbar.enabled");
     if (devToolbarEnabled) {
-      document.getElementById("menu_devToolbar").hidden = false;
-      document.getElementById("Tools:DevToolbar").removeAttribute("disabled");
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_DevToolbar");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
       document.getElementById("Tools:DevToolbarFocus").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_devToolbar").hidden = false;
-#endif
 
       // Show the toolbar if it was previously visible
       if (gPrefService.getBoolPref("devtools.toolbar.visible")) {
         DeveloperToolbar.show(false);
       }
     }
 
     // Enable Inspector?
     let enabled = gPrefService.getBoolPref("devtools.inspector.enabled");
     if (enabled) {
-      document.getElementById("menu_pageinspect").hidden = false;
-      document.getElementById("Tools:Inspect").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_pageInspect").hidden = false;
-#endif
-      document.getElementById("developer-toolbar-inspector").hidden = false;
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Inspect");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
     }
 
     // Enable Debugger?
     let enabled = gPrefService.getBoolPref("devtools.debugger.enabled");
     if (enabled) {
-      document.getElementById("menu_debugger").hidden = false;
-      document.getElementById("Tools:Debugger").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_debugger").hidden = false;
-#endif
-      document.getElementById("developer-toolbar-debugger").hidden = false;
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Debugger");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
     }
 
     // Enable Remote Debugger?
     let enabled = gPrefService.getBoolPref("devtools.debugger.remote-enabled");
     if (enabled) {
-      document.getElementById("menu_remoteDebugger").hidden = false;
-      document.getElementById("Tools:RemoteDebugger").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_remoteDebugger").hidden = false;
-#endif
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_RemoteDebugger");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
     }
 
     // Enable Chrome Debugger?
     let enabled = gPrefService.getBoolPref("devtools.chrome.enabled") &&
                   gPrefService.getBoolPref("devtools.debugger.chrome-enabled") &&
                   gPrefService.getBoolPref("devtools.debugger.remote-enabled");
     if (enabled) {
-      document.getElementById("menu_chromeDebugger").hidden = false;
-      document.getElementById("Tools:ChromeDebugger").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_chromeDebugger").hidden = false;
-#endif
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ChromeDebugger");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
     }
 
     // Enable Error Console?
     // XXX Temporarily always-enabled, see bug 601201
     let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled");
     if (consoleEnabled) {
-      document.getElementById("javascriptConsole").hidden = false;
-      document.getElementById("key_errorConsole").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_errorConsole").hidden = false;
-#endif
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ErrorConsole");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
     }
 
     // Enable Scratchpad in the UI, if the preference allows this.
     let scratchpadEnabled = gPrefService.getBoolPref(Scratchpad.prefEnabledName);
     if (scratchpadEnabled) {
-      document.getElementById("menu_scratchpad").hidden = false;
-      document.getElementById("Tools:Scratchpad").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_scratchpad").hidden = false;
-#endif
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Scratchpad");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
     }
 
     // Enable Style Editor?
     let styleEditorEnabled = gPrefService.getBoolPref(StyleEditor.prefEnabledName);
     if (styleEditorEnabled) {
-      document.getElementById("menu_styleeditor").hidden = false;
-      document.getElementById("Tools:StyleEditor").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_styleeditor").hidden = false;
-#endif
-      // We don't show the Style Editor button in the developer toolbar for now.
-      // See bug 771203
-      // document.getElementById("developer-toolbar-styleeditor").hidden = false;
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_StyleEditor");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
     }
 
 #ifdef MENUBAR_CAN_AUTOHIDE
     // If the user (or the locale) hasn't enabled the top-level "Character
     // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
     // hide it.
     if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
                                                Ci.nsIPrefLocalizedString).data)
       document.getElementById("appmenu_charsetMenu").hidden = true;
 #endif
 
     // Enable Responsive UI?
     let responsiveUIEnabled = gPrefService.getBoolPref("devtools.responsiveUI.enabled");
     if (responsiveUIEnabled) {
-      document.getElementById("menu_responsiveUI").hidden = false;
-      document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
-#ifdef MENUBAR_CAN_AUTOHIDE
-      document.getElementById("appmenu_responsiveUI").hidden = false;
-#endif
+      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ResponsiveUI");
+      broadcaster.removeAttribute("disabled");
+      broadcaster.removeAttribute("hidden");
     }
 
     let appMenuButton = document.getElementById("appmenu-button");
     let appMenuPopup = document.getElementById("appmenu-popup");
     if (appMenuButton && appMenuPopup) {
       let appMenuOpening = null;
       appMenuButton.addEventListener("mousedown", function(event) {
         if (event.button == 0)
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1150,34 +1150,45 @@
           <stack class="gclitoolbar-stack-node" flex="1">
             <hbox class="gclitoolbar-prompt">
               <label class="gclitoolbar-prompt-label">&#187;</label>
             </hbox>
             <hbox class="gclitoolbar-complete-node"/>
             <textbox class="gclitoolbar-input-node" rows="1"/>
           </stack>
           <toolbarbutton id="developer-toolbar-webconsole"
-                         label="&webConsoleButton.label;"
                          class="developer-toolbar-button"
-                         command="Tools:WebConsole"/>
+                         observes="devtoolsMenuBroadcaster_WebConsole"/>
           <toolbarbutton id="developer-toolbar-inspector"
-                         label="&inspectorButton.label;"
                          class="developer-toolbar-button"
-                         hidden="true"
-                         command="Tools:Inspect"/>
+                         observes="devtoolsMenuBroadcaster_Inspect"/>
           <toolbarbutton id="developer-toolbar-styleeditor"
-                         label="&styleeditor.label;"
                          class="developer-toolbar-button"
-                         hidden="true"
-                         command="Tools:StyleEditor"/>
+                         observes="devtoolsMenuBroadcaster_StyleEditor"/>
           <toolbarbutton id="developer-toolbar-debugger"
-                         label="&debuggerMenu.label2;"
+                         class="developer-toolbar-button"
+                         observes="devtoolsMenuBroadcaster_Debugger"/>
+          <toolbarbutton id="developer-toolbar-other-tools"
+                         type="menu"
                          class="developer-toolbar-button"
-                         hidden="true"
-                         command="Tools:Debugger"/>
+                         label="&devToolbarOtherToolsButton.label;">
+            <menupopup position="before_end">
+               <menuitem observes="devtoolsMenuBroadcaster_DevToolbar"/>
+               <menuitem observes="devtoolsMenuBroadcaster_ResponsiveUI"/>
+               <menuitem observes="devtoolsMenuBroadcaster_RemoteDebugger"/>
+               <menuitem observes="devtoolsMenuBroadcaster_ChromeDebugger"/>
+               <menuitem observes="devtoolsMenuBroadcaster_Scratchpad"/>
+               <menuitem observes="devtoolsMenuBroadcaster_StyleEditor"/>
+               <menuitem observes="devtoolsMenuBroadcaster_PageSource"/>
+               <menuitem observes="devtoolsMenuBroadcaster_ErrorConsole"/>
+               <menuseparator/>
+               <menuitem observes="devtoolsMenuBroadcaster_GetMoreTools"/>
+            </menupopup>
+          </toolbarbutton>
+
 #ifndef XP_MACOSX
           <toolbarbutton id="developer-toolbar-closebutton"
                          class="devtools-closebutton"
                          oncommand="DeveloperToolbar.hide();"
                          tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
 #endif
    </toolbar>
 
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -383,17 +383,17 @@ Rule.prototype = {
     }
     this._inheritedSource = "";
     if (this.inherited) {
       let eltText = this.inherited.tagName.toLowerCase();
       if (this.inherited.id) {
         eltText += "#" + this.inherited.id;
       }
       this._inheritedSource =
-        CssLogic._strings.formatStringFromName("rule.inheritedSource", [eltText], 1);
+        CssLogic._strings.formatStringFromName("rule.inheritedFrom", [eltText], 1);
     }
     return this._inheritedSource;
   },
 
   /**
    * The rule's stylesheet.
    */
   get sheet()
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -236,16 +236,17 @@ These should match what Safari and other
 
 <!ENTITY inspectCloseButton.tooltiptext "Close Inspector">
 
 <!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
 <!ENTITY devToolbarMenu.label              "Developer Toolbar">
 <!ENTITY devToolbarMenu.accesskey          "v">
 <!ENTITY devToolbar.keycode                "VK_F2">
 <!ENTITY devToolbar.keytext                "F2">
+<!ENTITY devToolbarOtherToolsButton.label  "More Tools">
 
 <!ENTITY webConsoleButton.label "Web Console">
 <!ENTITY inspectorButton.label "Inspector">
 
 <!ENTITY inspectorHTMLCopyInner.label       "Copy Inner HTML">
 <!ENTITY inspectorHTMLCopyInner.accesskey   "I">
 
 <!ENTITY inspectorHTMLCopyOuter.label       "Copy Outer HTML">
--- a/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties
@@ -25,21 +25,21 @@ rule.status.UNMATCHED=Unmatched
 
 # LOCALIZATION NOTE (rule.sourceElement, rule.sourceInline): For each
 # style property the panel shows the rules which hold that specific property.
 # For every rule, the rule source is also displayed: a rule can come from a
 # file, from the same page (inline), or from the element itself (element).
 rule.sourceInline=inline
 rule.sourceElement=element
 
-# LOCALIZATION NOTE (rule.inheritedSource): Shown for CSS rules
-# that were inherited from a parent node.  Will be passed a node
-# identifier and a source location.
+# LOCALIZATION NOTE (rule.inheritedFrom): Shown for CSS rules
+# that were inherited from a parent node. Will be passed a node
+# identifier of the parent node.
 # e.g "Inherited from body#bodyID"
-rule.inheritedSource=Inherited from %S
+rule.inheritedFrom=Inherited from %S
 
 # LOCALIZATION NOTE (style.highlighter.button): These strings are used inside
 # sidebar of the Highlighter for the style inspector button.
 # "Computed" refers to the Computed Style of the element.
 style.highlighter.button.label2=Computed
 style.highlighter.accesskey2=C
 style.highlighter.button.tooltip2=Inspect element computed styles
 
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2560,16 +2560,17 @@ stack[anonid=browserStack][responsivemod
   border-radius: 3px;
   color: inherit;
   border: 1px solid transparent;
   margin: 0 5px;
   padding: 0 10px;
   list-style-image: url("chrome://browser/skin/devtools/tools-icons-small.png");
 }
 
+.developer-toolbar-button[open=true],
 .developer-toolbar-button:active:hover,
 .developer-toolbar-button[checked=true] {
   border-color: hsla(210,8%,5%,.6);
   background: rgba(0,0,0,.6);
   box-shadow: 0 1px 2px rgba(0,0,0,.5) inset, 0 1px 0 hsla(210,16%,76%,.15);
 }
 
 .developer-toolbar-button[checked=true] {
@@ -2589,16 +2590,24 @@ stack[anonid=browserStack][responsivemod
 #developer-toolbar-styleeditor {
   -moz-image-region: rect(32px, 16px, 48px, 0);
 }
 
 #developer-toolbar-debugger {
   -moz-image-region: rect(48px, 16px, 64px, 0);
 }
 
+#developer-toolbar-other-tools {
+  -moz-image-region: rect(64px, 16px, 80px, 0);
+}
+
+#developer-toolbar-other-tools > .toolbarbutton-menu-dropmarker {
+  display: none;
+}
+
 /* Error counter */
 
 #developer-toolbar-webconsole[error-count]:before {
   color: #FDF3DE;
   min-width: 16px;
   text-shadow: none;
   background-image: -moz-linear-gradient(top, #B4211B, #8A1915);
   border-radius: 1px;
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3314,16 +3314,17 @@ stack[anonid=browserStack][responsivemod
   border-radius: @toolbarbuttonCornerRadius@;
   color: inherit;
   border: 1px solid transparent;
   margin: 0 5px;
   padding: 0 10px;
   list-style-image: url("chrome://browser/skin/devtools/tools-icons-small.png");
 }
 
+.developer-toolbar-button[open=true],
 .developer-toolbar-button:active:hover,
 .developer-toolbar-button[checked=true] {
   border-color: hsla(210,8%,5%,.6);
   background: rgba(0,0,0,.6);
   box-shadow: 0 1px 2px rgba(0,0,0,.5) inset, 0 1px 0 hsla(210,16%,76%,.15);
 }
 
 .developer-toolbar-button[checked=true] {
@@ -3343,16 +3344,24 @@ stack[anonid=browserStack][responsivemod
 #developer-toolbar-styleeditor {
   -moz-image-region: rect(32px, 16px, 48px, 0);
 }
 
 #developer-toolbar-debugger {
   -moz-image-region: rect(48px, 16px, 64px, 0);
 }
 
+#developer-toolbar-other-tools {
+  -moz-image-region: rect(64px, 16px, 80px, 0);
+}
+
+#developer-toolbar-other-tools > .toolbarbutton-menu-dropmarker {
+  display: none;
+}
+
 /* Error counter */
 
 #developer-toolbar-webconsole[error-count]:before {
   color: #FDF3DE;
   min-width: 16px;
   text-shadow: none;
   background-image: -moz-linear-gradient(top, #B4211B, #8A1915);
   border-radius: 1px;
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3241,16 +3241,17 @@ stack[anonid=browserStack][responsivemod
   border-radius: 3px;
   color: inherit;
   border: 1px solid transparent;
   margin: 0 5px;
   padding: 0 10px;
   list-style-image: url("chrome://browser/skin/devtools/tools-icons-small.png");
 }
 
+.developer-toolbar-button[open=true],
 .developer-toolbar-button:active:hover,
 .developer-toolbar-button[checked=true] {
   border-color: hsla(210,8%,5%,.6);
   background: rgba(0,0,0,.6);
   box-shadow: 0 1px 2px rgba(0,0,0,.5) inset, 0 1px 0 hsla(210,16%,76%,.1);
 }
 
 .developer-toolbar-button[checked=true] {
@@ -3270,16 +3271,24 @@ stack[anonid=browserStack][responsivemod
 #developer-toolbar-styleeditor {
   -moz-image-region: rect(32px, 16px, 48px, 0);
 }
 
 #developer-toolbar-debugger {
   -moz-image-region: rect(48px, 16px, 64px, 0);
 }
 
+#developer-toolbar-other-tools {
+  -moz-image-region: rect(64px, 16px, 80px, 0);
+}
+
+#developer-toolbar-other-tools > .toolbarbutton-menu-dropmarker {
+  display: none;
+}
+
 /* Error counter */
 
 #developer-toolbar-webconsole[error-count]:before {
   color: #FDF3DE;
   min-width: 16px;
   text-shadow: none;
   background-image: -moz-linear-gradient(top, #B4211B, #8A1915);
   border-radius: 1px;
--- a/testing/xpcshell/xpcshell.ini
+++ b/testing/xpcshell/xpcshell.ini
@@ -18,16 +18,17 @@
 [include:dom/indexedDB/test/unit/xpcshell.ini]
 [include:content/xtf/test/unit/xpcshell.ini]
 [include:docshell/test/unit/xpcshell.ini]
 [include:docshell/test/unit_ipc/xpcshell.ini]
 [include:embedding/tests/unit/xpcshell.ini]
 [include:toolkit/components/commandlines/test/unit/xpcshell.ini]
 [include:toolkit/components/contentprefs/tests/unit/xpcshell.ini]
 [include:toolkit/devtools/debugger/tests/unit/xpcshell.ini]
+[include:toolkit/devtools/sourcemap/tests/unit/xpcshell.ini]
 [include:toolkit/components/passwordmgr/test/unit/xpcshell.ini]
 # Bug 676989: tests hang on Android
 skip-if = os == "android"
 [include:toolkit/components/places/tests/migration/xpcshell.ini]
 [include:toolkit/components/places/tests/autocomplete/xpcshell.ini]
 [include:toolkit/components/places/tests/inline/xpcshell.ini]
 [include:toolkit/components/places/tests/expiration/xpcshell.ini]
 [include:toolkit/components/places/tests/favicons/xpcshell.ini]
--- a/toolkit/devtools/Makefile.in
+++ b/toolkit/devtools/Makefile.in
@@ -6,11 +6,12 @@ DEPTH     = ../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(topsrcdir)/config/config.mk
 
 PARALLEL_DIRS += \
   debugger \
+  sourcemap \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/Makefile.in
@@ -0,0 +1,19 @@
+#
+# 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/.
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+# No tests yet
+TEST_DIRS += tests
+
+include $(topsrcdir)/config/rules.mk
+
+libs::
+	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/SourceMap.jsm
@@ -0,0 +1,1045 @@
+/* -*- 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
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+var EXPORTED_SYMBOLS = [ "SourceMapConsumer", "SourceMapGenerator", "SourceNode" ];
+
+Components.utils.import('resource:///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) {
+
+  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');
+
+  // TODO:  bug 673487
+  //
+  // Sometime in the future, if we decide we need to be able to query where in
+  // the generated source a piece of the original code came from, we may want to
+  // add a slot `_originalMappings` which would be an object keyed by the
+  // original source and whose value would be an array of mappings ordered by
+  // original line/col rather than generated (which is what we have now in
+  // `_generatedMappings`).
+
+  /**
+   * A SourceMapConsumer instance represents a parsed source map which we can
+   * query for information about the original file positions by giving it a file
+   * position in the generated source.
+   *
+   * The only parameter is the raw source map (either as a JSON string, or
+   * already parsed to an object). According to the spec, source maps have the
+   * following attributes:
+   *
+   *   - version: Which version of the source map spec this map is following.
+   *   - sources: An array of URLs to the original source files.
+   *   - names: An array of identifiers which can be referrenced by individual mappings.
+   *   - sourceRoot: Optional. The URL root from which all sources are relative.
+   *   - mappings: A string of base64 VLQs which contain the actual mappings.
+   *   - file: The generated file this source map is associated with.
+   *
+   * Here is an example source map, taken from the source map spec[0]:
+   *
+   *     {
+   *       version : 3,
+   *       file: "out.js",
+   *       sourceRoot : "",
+   *       sources: ["foo.js", "bar.js"],
+   *       names: ["src", "maps", "are", "fun"],
+   *       mappings: "AA,AB;;ABCDE;"
+   *     }
+   *
+   * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
+   */
+  function SourceMapConsumer(aSourceMap) {
+    var sourceMap = aSourceMap;
+    if (typeof aSourceMap === 'string') {
+      sourceMap = JSON.parse(aSourceMap);
+    }
+
+    var version = util.getArg(sourceMap, 'version');
+    var sources = util.getArg(sourceMap, 'sources');
+    var names = util.getArg(sourceMap, 'names');
+    var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
+    var mappings = util.getArg(sourceMap, 'mappings');
+    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._generatedMappings` 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,
+    //       source: The path to the original source file that generated this
+    //               chunk of code,
+    //       originalLine: The line number in the original source that
+    //                     corresponds to this chunk of generated code,
+    //       originalColumn: The column number in the original source that
+    //                       corresponds to this chunk of generated code,
+    //       name: The name of the original symbol which generated this chunk of
+    //             code.
+    //     }
+    //
+    // All properties except for `generatedLine` and `generatedColumn` can be
+    // `null`.
+    this._generatedMappings = [];
+    this._parseMappings(mappings, sourceRoot);
+  }
+
+  /**
+   * The version of the source mapping spec that we are consuming.
+   */
+  SourceMapConsumer.prototype._version = 3;
+
+  /**
+   * 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;
+      var previousOriginalColumn = 0;
+      var previousSource = 0;
+      var previousName = 0;
+      var mappingSeparator = /^[,;]/;
+      var str = aStr;
+      var mapping;
+      var temp;
+
+      while (str.length > 0) {
+        if (str.charAt(0) === ';') {
+          generatedLine++;
+          str = str.slice(1);
+          previousGeneratedColumn = 0;
+        }
+        else if (str.charAt(0) === ',') {
+          str = str.slice(1);
+        }
+        else {
+          mapping = {};
+          mapping.generatedLine = generatedLine;
+
+          // Generated column.
+          temp = base64VLQ.decode(str);
+          mapping.generatedColumn = previousGeneratedColumn + temp.value;
+          previousGeneratedColumn = mapping.generatedColumn;
+          str = temp.rest;
+
+          if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+            // Original source.
+            temp = base64VLQ.decode(str);
+            if (aSourceRoot) {
+              mapping.source = util.join(aSourceRoot, this._sources.at(previousSource + temp.value));
+            }
+            else {
+              mapping.source = this._sources.at(previousSource + temp.value);
+            }
+            previousSource += temp.value;
+            str = temp.rest;
+            if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+              throw new Error('Found a source, but no line and column');
+            }
+
+            // Original line.
+            temp = base64VLQ.decode(str);
+            mapping.originalLine = previousOriginalLine + temp.value;
+            previousOriginalLine = mapping.originalLine;
+            // Lines are stored 0-based
+            mapping.originalLine += 1;
+            str = temp.rest;
+            if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+              throw new Error('Found a source and line, but no column');
+            }
+
+            // Original column.
+            temp = base64VLQ.decode(str);
+            mapping.originalColumn = previousOriginalColumn + temp.value;
+            previousOriginalColumn = mapping.originalColumn;
+            str = temp.rest;
+
+            if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+              // Original name.
+              temp = base64VLQ.decode(str);
+              mapping.name = this._names.at(previousName + temp.value);
+              previousName += temp.value;
+              str = temp.rest;
+            }
+          }
+
+          this._generatedMappings.push(mapping);
+        }
+      }
+    };
+
+  /**
+   * Returns the original source, line, and column information for the generated
+   * source's line and column positions provided. The only argument is an object
+   * with the following properties:
+   *
+   *   - line: The line number in the generated source.
+   *   - column: The column number in the generated source.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - source: The original source file, or null.
+   *   - line: The line number in the original source, or null.
+   *   - column: The column number in the original source, or null.
+   *   - name: The original identifier, or null.
+   */
+  SourceMapConsumer.prototype.originalPositionFor =
+    function SourceMapConsumer_originalPositionFor(aArgs) {
+      // To return the original position, we must first find the mapping for the
+      // given generated position and then return the original position it
+      // points to. Because the mappings are sorted by generated line/column, we
+      // can use binary search to find the best mapping.
+
+      // To perform a binary search on the mappings, we must be able to compare
+      // two mappings.
+      function compare(mappingA, mappingB) {
+        var cmp = mappingA.generatedLine - mappingB.generatedLine;
+        return cmp === 0
+          ? mappingA.generatedColumn - mappingB.generatedColumn
+          : cmp;
+      }
+
+      // This is the mock of the mapping we are looking for: the needle in the
+      // haystack of mappings.
+      var needle = {
+        generatedLine: util.getArg(aArgs, 'line'),
+        generatedColumn: util.getArg(aArgs, 'column')
+      };
+
+      if (needle.generatedLine <= 0) {
+        throw new TypeError('Line must be greater than or equal to 1.');
+      }
+      if (needle.generatedColumn < 0) {
+        throw new TypeError('Column must be greater than or equal to 0.');
+      }
+
+      var mapping = binarySearch.search(needle, this._generatedMappings, compare);
+
+      if (mapping) {
+        return {
+          source: util.getArg(mapping, 'source', null),
+          line: util.getArg(mapping, 'originalLine', null),
+          column: util.getArg(mapping, 'originalColumn', null),
+          name: util.getArg(mapping, 'name', null)
+        };
+      }
+
+      return {
+        source: null,
+        line: null,
+        column: null,
+        name: null
+      };
+
+    };
+
+  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) {
+
+  /**
+   * 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
+   * from the object. If this is not specified and the property is missing, an
+   * error will be thrown.
+   */
+  function getArg(aArgs, aName, aDefaultValue) {
+    if (aName in aArgs) {
+      return aArgs[aName];
+    } else if (arguments.length === 3) {
+      return aDefaultValue;
+    } else {
+      throw new Error('"' + aName + '" is a required argument.');
+    }
+  }
+  exports.getArg = getArg;
+
+  function join(aRoot, aPath) {
+    return aPath.charAt(0) === '/'
+      ? aPath
+      : aRoot.replace(/\/*$/, '') + '/' + aPath;
+  }
+  exports.join = join;
+
+});
+/* -*- 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) {
+
+  /**
+   * 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.
+   * @param aCompare Function which takes two elements and returns -1, 0, or 1.
+   */
+  function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
+    // This function terminates when one of the following is true:
+    //
+    //   1. We find the exact element we are looking for.
+    //
+    //   2. We did not find the exact element, but we can return the next
+    //      closest element that is less than that element.
+    //
+    //   3. We did not find the exact element, and there is no next-closest
+    //      element which is less than the one we are searching for, so we
+    //      return null.
+    var mid = Math.floor((aHigh - aLow) / 2) + aLow;
+    var cmp = aCompare(aNeedle, aHaystack[mid]);
+    if (cmp === 0) {
+      // Found the element we are looking for.
+      return aHaystack[mid];
+    }
+    else if (cmp > 0) {
+      // aHaystack[mid] is greater than our needle.
+      if (aHigh - mid > 1) {
+        // The element is in the upper half.
+        return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
+      }
+      // We did not find an exact match, return the next closest one
+      // (termination case 2).
+      return aHaystack[mid];
+    }
+    else {
+      // aHaystack[mid] is less than our needle.
+      if (mid - aLow > 1) {
+        // The element is in the lower half.
+        return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
+      }
+      // The exact needle element was not found in this haystack. Determine if
+      // we are in termination case (2) or (3) and return the appropriate thing.
+      return aLow < 0
+        ? null
+        : aHaystack[aLow];
+    }
+  }
+
+  /**
+   * This is an implementation of binary search which will always try and return
+   * the next lowest value checked if there is no exact hit. This is because
+   * mappings between original and generated line/col pairs are single points,
+   * and there is an implicit region between each of them, so a miss just means
+   * that you aren't on the very start of a region.
+   *
+   * @param aNeedle The element you are looking for.
+   * @param aHaystack The array that is being searched.
+   * @param aCompare A function which takes the needle and an element in the
+   *     array and returns -1, 0, or 1 depending on whether the needle is less
+   *     than, equal to, or greater than the element, respectively.
+   */
+  exports.search = function search(aNeedle, aHaystack, aCompare) {
+    return aHaystack.length > 0
+      ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
+      : null;
+  };
+
+});
+/* -*- 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) {
+
+  /**
+   * 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() {
+    this._array = [];
+    this._set = {};
+  }
+
+  /**
+   * Static method for creating ArraySet instances from an existing array.
+   */
+  ArraySet.fromArray = function ArraySet_fromArray(aArray) {
+    var set = new ArraySet();
+    for (var i = 0, len = aArray.length; i < len; i++) {
+      set.add(aArray[i]);
+    }
+    return set;
+  };
+
+  /**
+   * Add the given string to this set.
+   *
+   * @param String str
+   */
+  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;
+  };
+
+  /**
+   * Is the given string a member of this set?
+   *
+   * @param String str
+   */
+  ArraySet.prototype.has = function ArraySet_has(aStr) {
+    return Object.prototype.hasOwnProperty.call(this._set, aStr);
+  };
+
+  /**
+   * What is the index of the given string in the array?
+   *
+   * @param String str
+   */
+  ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
+    if (this.has(aStr)) {
+      return this._set[aStr];
+    }
+    throw new Error('"' + aStr + '" is not in the set.');
+  };
+
+  /**
+   * What is the element at the given index?
+   *
+   * @param Number idx
+   */
+  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);
+  };
+
+  /**
+   * Returns the array representation of this set (which has the proper indices
+   * indicated by indexOf). Note that this is a copy of the internal array used
+   * for storing the members so that no one can mess with internal state.
+   */
+  ArraySet.prototype.toArray = function ArraySet_toArray() {
+    return this._array.slice();
+  };
+
+  exports.ArraySet = ArraySet;
+
+});
+/* -*- 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
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ *  * Neither the name of Google Inc. nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * 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) {
+
+  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.
+  //
+  //   Continuation
+  //   |    Sign
+  //   |    |
+  //   V    V
+  //   101011
+
+  var VLQ_BASE_SHIFT = 5;
+
+  // binary: 100000
+  var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
+
+  // binary: 011111
+  var VLQ_BASE_MASK = VLQ_BASE - 1;
+
+  // binary: 100000
+  var VLQ_CONTINUATION_BIT = VLQ_BASE;
+
+  /**
+   * Converts from a two-complement value to a value where the sign bit is
+   * is placed in the least significant bit.  For example, as decimals:
+   *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+   *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+   */
+  function toVLQSigned(aValue) {
+    return aValue < 0
+      ? ((-aValue) << 1) + 1
+      : (aValue << 1) + 0;
+  }
+
+  /**
+   * Converts to a two-complement value from a value where the sign bit is
+   * is placed in the least significant bit.  For example, as decimals:
+   *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+   *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+   */
+  function fromVLQSigned(aValue) {
+    var isNegative = (aValue & 1) === 1;
+    var shifted = aValue >> 1;
+    return isNegative
+      ? -shifted
+      : shifted;
+  }
+
+  /**
+   * Returns the base 64 VLQ encoded value.
+   */
+  exports.encode = function base64VLQ_encode(aValue) {
+    var encoded = "";
+    var digit;
+
+    var vlq = toVLQSigned(aValue);
+
+    do {
+      digit = vlq & VLQ_BASE_MASK;
+      vlq >>>= VLQ_BASE_SHIFT;
+      if (vlq > 0) {
+        // There are still more digits in this value, so we must make sure the
+        // continuation bit is marked.
+        digit |= VLQ_CONTINUATION_BIT;
+      }
+      encoded += base64.encode(digit);
+    } while (vlq > 0);
+
+    return encoded;
+  };
+
+  /**
+   * Decodes the next base 64 VLQ value from the given string and returns the
+   * value and the rest of the string.
+   */
+  exports.decode = function base64VLQ_decode(aStr) {
+    var i = 0;
+    var strLen = aStr.length;
+    var result = 0;
+    var shift = 0;
+    var continuation, digit;
+
+    do {
+      if (i >= strLen) {
+        throw new Error("Expected more digits in base 64 VLQ value.");
+      }
+      digit = base64.decode(aStr.charAt(i++));
+      continuation = !!(digit & VLQ_CONTINUATION_BIT);
+      digit &= VLQ_BASE_MASK;
+      result = result + (digit << shift);
+      shift += VLQ_BASE_SHIFT;
+    } while (continuation);
+
+    return {
+      value: fromVLQSigned(result),
+      rest: aStr.slice(i)
+    };
+  };
+
+});
+/* -*- 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) {
+
+  var charToIntMap = {};
+  var intToCharMap = {};
+
+  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+    .split('')
+    .forEach(function (ch, index) {
+      charToIntMap[ch] = index;
+      intToCharMap[index] = ch;
+    });
+
+  /**
+   * Encode an integer in the range of 0 to 63 to a single base 64 digit.
+   */
+  exports.encode = function base64_encode(aNumber) {
+    if (aNumber in intToCharMap) {
+      return intToCharMap[aNumber];
+    }
+    throw new TypeError("Must be between 0 and 63: " + aNumber);
+  };
+
+  /**
+   * Decode a single base 64 digit to an integer.
+   */
+  exports.decode = function base64_decode(aChar) {
+    if (aChar in charToIntMap) {
+      return charToIntMap[aChar];
+    }
+    throw new TypeError("Not a valid base 64 digit: " + aChar);
+  };
+
+});
+/* -*- 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) {
+
+  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
+   * with the following properties:
+   *
+   *   - file: The filename of the generated source.
+   *   - sourceRoot: An optional root for all URLs in this source map.
+   */
+  function SourceMapGenerator(aArgs) {
+    this._file = util.getArg(aArgs, 'file');
+    this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
+    this._sources = new ArraySet();
+    this._names = new ArraySet();
+    this._mappings = [];
+  }
+
+  SourceMapGenerator.prototype._version = 3;
+
+  /**
+   * Add a single mapping from original source line and column to the generated
+   * source's line and column for this source map being created. The mapping
+   * object should have the following properties:
+   *
+   *   - generated: An object with the generated line and column positions.
+   *   - original: An object with the original line and column positions.
+   *   - source: The original source file (relative to the sourceRoot).
+   *   - name: An optional original token name for this mapping.
+   */
+  SourceMapGenerator.prototype.addMapping =
+    function SourceMapGenerator_addMapping(aArgs) {
+      var generated = util.getArg(aArgs, 'generated');
+      var original = util.getArg(aArgs, 'original', null);
+      var source = util.getArg(aArgs, 'source', null);
+      var name = util.getArg(aArgs, 'name', null);
+
+      this._validateMapping(generated, original, source, name);
+
+      if (source && !this._sources.has(source)) {
+        this._sources.add(source);
+      }
+
+      if (name && !this._names.has(name)) {
+        this._names.add(name);
+      }
+
+      this._mappings.push({
+        generated: generated,
+        original: original,
+        source: source,
+        name: name
+      });
+    };
+
+  /**
+   * A mapping can have one of the three levels of data:
+   *
+   *   1. Just the generated position.
+   *   2. The Generated position, original position, and original source.
+   *   3. Generated and original position, original source, as well as a name
+   *      token.
+   *
+   * To maintain consistency, we validate that any new mapping being added falls
+   * in to one of these categories.
+   */
+  SourceMapGenerator.prototype._validateMapping =
+    function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
+                                                aName) {
+      if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+          && aGenerated.line > 0 && aGenerated.column >= 0
+          && !aOriginal && !aSource && !aName) {
+        // Case 1.
+        return;
+      }
+      else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+               && aOriginal && 'line' in aOriginal && 'column' in aOriginal
+               && aGenerated.line > 0 && aGenerated.column >= 0
+               && aOriginal.line > 0 && aOriginal.column >= 0
+               && aSource) {
+        // Cases 2 and 3.
+        return;
+      }
+      else {
+        throw new Error('Invalid mapping.');
+      }
+    };
+
+  /**
+   * Serialize the accumulated mappings in to the stream of base 64 VLQs
+   * specified by the source map format.
+   */
+  SourceMapGenerator.prototype._serializeMappings =
+    function SourceMapGenerator_serializeMappings() {
+      var previousGeneratedColumn = 0;
+      var previousGeneratedLine = 1;
+      var previousOriginalColumn = 0;
+      var previousOriginalLine = 0;
+      var previousName = 0;
+      var previousSource = 0;
+      var result = '';
+      var mapping;
+
+      // The mappings must be guarenteed to be in sorted order before we start
+      // serializing them or else the generated line numbers (which are defined
+      // via the ';' separators) will be all messed up. Note: it might be more
+      // performant to maintain the sorting as we insert them, rather than as we
+      // serialize them, but the big O is the same either way.
+      this._mappings.sort(function (mappingA, mappingB) {
+        var cmp = mappingA.generated.line - mappingB.generated.line;
+        return cmp === 0
+          ? mappingA.generated.column - mappingB.generated.column
+          : cmp;
+      });
+
+      for (var i = 0, len = this._mappings.length; i < len; i++) {
+        mapping = this._mappings[i];
+
+        if (mapping.generated.line !== previousGeneratedLine) {
+          previousGeneratedColumn = 0;
+          while (mapping.generated.line !== previousGeneratedLine) {
+            result += ';';
+            previousGeneratedLine++;
+          }
+        }
+        else {
+          if (i > 0) {
+            result += ',';
+          }
+        }
+
+        result += base64VLQ.encode(mapping.generated.column
+                                   - previousGeneratedColumn);
+        previousGeneratedColumn = mapping.generated.column;
+
+        if (mapping.source && mapping.original) {
+          result += base64VLQ.encode(this._sources.indexOf(mapping.source)
+                                     - previousSource);
+          previousSource = this._sources.indexOf(mapping.source);
+
+          // lines are stored 0-based in SourceMap spec version 3
+          result += base64VLQ.encode(mapping.original.line - 1
+                                     - previousOriginalLine);
+          previousOriginalLine = mapping.original.line - 1;
+
+          result += base64VLQ.encode(mapping.original.column
+                                     - previousOriginalColumn);
+          previousOriginalColumn = mapping.original.column;
+
+          if (mapping.name) {
+            result += base64VLQ.encode(this._names.indexOf(mapping.name)
+                                       - previousName);
+            previousName = this._names.indexOf(mapping.name);
+          }
+        }
+      }
+
+      return result;
+    };
+
+  /**
+   * Render the source map being generated to a string.
+   */
+  SourceMapGenerator.prototype.toString =
+    function SourceMapGenerator_toString() {
+      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);
+    };
+
+  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) {
+
+  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.
+   *
+   * @param aLine The original line number.
+   * @param aColumn The original column number.
+   * @param aSource The original source's filename.
+   * @param aChunks Optional. An array of strings which are snippets of
+   *        generated JS, or other SourceNodes.
+   */
+  function SourceNode(aLine, aColumn, aSource, aChunks) {
+    this.children = [];
+    this.line = aLine;
+    this.column = aColumn;
+    this.source = aSource;
+    if (aChunks != null) this.add(aChunks);
+  }
+
+  /**
+   * Add a chunk of generated JS to this source node.
+   *
+   * @param aChunk A string snippet of generated JS code, another instance of
+   *        SourceNode, or an array where each member is one of those things.
+   */
+  SourceNode.prototype.add = function SourceNode_add(aChunk) {
+    if (Array.isArray(aChunk)) {
+      aChunk.forEach(function (chunk) {
+        this.add(chunk);
+      }, this);
+    }
+    else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+      this.children.push(aChunk);
+    }
+    else {
+      throw new TypeError(
+        "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+      );
+    }
+    return this;
+  };
+
+  /**
+   * Add a chunk of generated JS to the beginning of this source node.
+   *
+   * @param aChunk A string snippet of generated JS code, another instance of
+   *        SourceNode, or an array where each member is one of those things.
+   */
+  SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
+    if (Array.isArray(aChunk)) {
+      for (var i = aChunk.length-1; i >= 0; i--) {
+        this.prepend(aChunk[i]);
+      }
+    }
+    else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+      this.children.unshift(aChunk);
+    }
+    else {
+      throw new TypeError(
+        "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+      );
+    }
+    return this;
+  };
+
+  /**
+   * Walk over the tree of JS snippets in this node and its children. The
+   * walking function is called once for each snippet of JS and is passed that
+   * snippet and the its original associated source's line/column location.
+   *
+   * @param aFn The traversal function.
+   */
+  SourceNode.prototype.walk = function SourceNode_walk(aFn) {
+    this.children.forEach(function (chunk) {
+      if (chunk instanceof SourceNode) {
+        chunk.walk(aFn);
+      }
+      else {
+        if (chunk !== '') {
+          aFn(chunk, { source: this.source, line: this.line, column: this.column });
+        }
+      }
+    }, this);
+  };
+
+  /**
+   * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
+   * each of `this.children`.
+   *
+   * @param aSep The separator.
+   */
+  SourceNode.prototype.join = function SourceNode_join(aSep) {
+    var newChildren;
+    var i;
+    var len = this.children.length
+    if (len > 0) {
+      newChildren = [];
+      for (i = 0; i < len-1; i++) {
+        newChildren.push(this.children[i]);
+        newChildren.push(aSep);
+      }
+      newChildren.push(this.children[i]);
+      this.children = newChildren;
+    }
+    return this;
+  };
+
+  /**
+   * Call String.prototype.replace on the very right-most source snippet. Useful
+   * for trimming whitespace from the end of a source node, etc.
+   *
+   * @param aPattern The pattern to replace.
+   * @param aReplacement The thing to replace the pattern with.
+   */
+  SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
+    var lastChild = this.children[this.children.length - 1];
+    if (lastChild instanceof SourceNode) {
+      lastChild.replaceRight(aPattern, aReplacement);
+    }
+    else if (typeof lastChild === 'string') {
+      this.children[this.children.lenth - 1] = lastChild.replace(aPattern, aReplacement);
+    }
+    else {
+      this.children.push(''.replace(aPattern, aReplacement));
+    }
+    return this;
+  };
+
+  /**
+   * Return the string representation of this source node. Walks over the tree
+   * and concatenates all the various snippets together to one string.
+   */
+  SourceNode.prototype.toString = function SourceNode_toString() {
+    var str = "";
+    this.walk(function (chunk) {
+      str += chunk;
+    });
+    return str;
+  };
+
+  /**
+   * Returns the string representation of this source node along with a source
+   * map.
+   */
+  SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
+    var generated = {
+      code: "",
+      line: 1,
+      column: 0
+    };
+    var map = new SourceMapGenerator(aArgs);
+    this.walk(function (chunk, original) {
+      generated.code += chunk;
+      if (original.source != null
+          && original.line != null
+          && original.column != null) {
+        map.addMapping({
+          source: original.source,
+          original: {
+            line: original.line,
+            column: original.column
+          },
+          generated: {
+            line: generated.line,
+            column: generated.column
+          }
+        });
+      }
+      chunk.split('').forEach(function (char) {
+        if (char === '\n') {
+          generated.line++;
+          generated.column = 0;
+        } else {
+          generated.column++;
+        }
+      });
+    });
+
+    return { code: generated.code, map: map };
+  };
+
+  exports.SourceNode = SourceNode;
+
+});
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+let SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
+let SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
+let SourceNode = require('source-map/source-node').SourceNode;
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/Makefile.in
@@ -0,0 +1,17 @@
+# 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/.
+
+DEPTH           = ../../../..
+topsrcdir       = @top_srcdir@
+srcdir          = @srcdir@
+VPATH           = @srcdir@
+relativesrcdir  = toolkit/devtools/sourcemap/tests
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE          = test_sourcemap
+
+XPCSHELL_TESTS  = unit
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/Utils.jsm
@@ -0,0 +1,152 @@
+/* -*- 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
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource:///modules/devtools/Require.jsm');
+Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
+
+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) {
+
+  let do_throw = function (msg) {
+    throw new Error(msg);
+  };
+
+  exports.init = function (throw_fn) {
+    do_throw = throw_fn;
+  };
+
+  exports.doesNotThrow = function (fn) {
+    try {
+      fn();
+    }
+    catch (e) {
+      do_throw(e.message);
+    }
+  };
+
+  exports.equal = function (actual, expected, msg) {
+    msg = msg || String(actual) + ' != ' + String(expected);
+    if (actual != expected) {
+      do_throw(msg);
+    }
+  };
+
+  exports.ok = function (val, msg) {
+    msg = msg || String(val) + ' is falsey';
+    if (!Boolean(val)) {
+      do_throw(msg);
+    }
+  };
+
+  exports.strictEqual = function (actual, expected, msg) {
+    msg = msg || String(actual) + ' !== ' + String(expected);
+    if (actual !== expected) {
+      do_throw(msg);
+    }
+  };
+
+  exports.throws = function (fn) {
+    try {
+      fn();
+      do_throw('Expected an error to be thrown, but it wasn\'t.');
+    }
+    catch (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('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);
+  //   };
+  //
+  // Here is two.js:
+  //
+  //   TWO.inc = function (n) {
+  //     return n + 1;
+  //   };
+  //
+  // And here is the generated code (min.js):
+  //
+  //   ONE.foo=function(a){return baz(a);};
+  //   TWO.inc=function(a){return a+1;};
+  exports.testMap = {
+    version: 3,
+    file: 'min.js',
+    names: ['bar', 'baz', 'n'],
+    sources: ['one.js', 'two.js'],
+    sourceRoot: '/the/root',
+    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+  };
+
+  function assertMapping(generatedLine, generatedColumn, originalSource,
+                         originalLine, originalColumn, name, map, assert) {
+    var mapping = map.originalPositionFor({
+      line: generatedLine,
+      column: generatedColumn
+    });
+    assert.equal(mapping.name, name,
+                 'Incorrect name, expected ' + JSON.stringify(name)
+                 + ', got ' + JSON.stringify(mapping.name));
+    assert.equal(mapping.line, originalLine,
+                 'Incorrect line, expected ' + JSON.stringify(originalLine)
+                 + ', got ' + JSON.stringify(mapping.line));
+    assert.equal(mapping.column, originalColumn,
+                 'Incorrect column, expected ' + JSON.stringify(originalColumn)
+                 + ', got ' + JSON.stringify(mapping.column));
+    assert.equal(mapping.source, originalSource,
+                 'Incorrect source, expected ' + JSON.stringify(originalSource)
+                 + ', got ' + JSON.stringify(mapping.source));
+  }
+  exports.assertMapping = assertMapping;
+
+});
+/* -*- 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
+ */
+function runSourceMapTests(modName, do_throw) {
+  let mod = require(modName);
+  let assert = require('test/source-map/assert');
+  let util = require('test/source-map/util');
+
+  assert.init(do_throw);
+
+  for (let k in mod) {
+    if (/^test/.test(k)) {
+      mod[k](assert, util);
+    }
+  }
+
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/head_sourcemap.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_array_set.js
@@ -0,0 +1,71 @@
+/*
+ * 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 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/test-array-set", ["require", "exports", "module"], function (require, exports, module) {
+
+  var ArraySet = require('source-map/array-set').ArraySet;
+
+  function makeTestSet() {
+    var set = new ArraySet();
+    for (var i = 0; i < 100; i++) {
+      set.add(String(i));
+    }
+    return set;
+  }
+
+  exports['test .has() membership'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.ok(set.has(String(i)));
+    }
+  };
+
+  exports['test .indexOf() elements'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.strictEqual(set.indexOf(String(i)), i);
+    }
+  };
+
+  exports['test .at() indexing'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.strictEqual(set.at(i), String(i));
+    }
+  };
+
+  exports['test creating from an array'] = function (assert, util) {
+    var set = ArraySet.fromArray(['foo', 'bar', 'baz', 'quux', 'hasOwnProperty']);
+
+    assert.ok(set.has('foo'));
+    assert.ok(set.has('bar'));
+    assert.ok(set.has('baz'));
+    assert.ok(set.has('quux'));
+    assert.ok(set.has('hasOwnProperty'));
+
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.indexOf('bar'), 1);
+    assert.strictEqual(set.indexOf('baz'), 2);
+    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');
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-array-set', do_throw);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_base64.js
@@ -0,0 +1,43 @@
+/*
+ * 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 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/test-base64", ["require", "exports", "module"], function (require, exports, module) {
+
+  var base64 = require('source-map/base64');
+
+  exports['test out of range encoding'] = function (assert, util) {
+    assert.throws(function () {
+      base64.encode(-1);
+    });
+    assert.throws(function () {
+      base64.encode(64);
+    });
+  };
+
+  exports['test out of range decoding'] = function (assert, util) {
+    assert.throws(function () {
+      base64.decode('=');
+    });
+  };
+
+  exports['test normal encoding and decoding'] = function (assert, util) {
+    for (var i = 0; i < 64; i++) {
+      assert.equal(base64.decode(base64.encode(i)), i);
+    }
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-base64', do_throw);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_base64_vlq.js
@@ -0,0 +1,32 @@
+/*
+ * 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 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/test-base64-vlq", ["require", "exports", "module"], function (require, exports, module) {
+
+  var base64VLQ = require('source-map/base64-vlq');
+
+  exports['test normal encoding and decoding'] = function (assert, util) {
+    var result;
+    for (var i = -255; i < 256; i++) {
+      result = base64VLQ.decode(base64VLQ.encode(i));
+      assert.ok(result);
+      assert.equal(result.value, i);
+      assert.equal(result.rest, "");
+    }
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-base64-vlq', do_throw);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_binary_search.js
@@ -0,0 +1,62 @@
+/*
+ * 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 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/test-binary-search", ["require", "exports", "module"], function (require, exports, module) {
+
+  var binarySearch = require('source-map/binary-search');
+
+  function numberCompare(a, b) {
+    return a - b;
+  }
+
+  exports['test too high'] = function (assert, util) {
+    var needle = 30;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.doesNotThrow(function () {
+      binarySearch.search(needle, haystack, numberCompare);
+    });
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 20);
+  };
+
+  exports['test too low'] = function (assert, util) {
+    var needle = 1;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.doesNotThrow(function () {
+      binarySearch.search(needle, haystack, numberCompare);
+    });
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), null);
+  };
+
+  exports['test exact search'] = function (assert, util) {
+    var needle = 4;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 4);
+  };
+
+  exports['test fuzzy search'] = function (assert, util) {
+    var needle = 19;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 18);
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-binary-search', do_throw);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_dog_fooding.js
@@ -0,0 +1,72 @@
+/*
+ * 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 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/test-dog-fooding", ["require", "exports", "module"], function (require, exports, module) {
+
+  var SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
+  var SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
+
+  exports['test eating our own dog food'] = function (assert, util) {
+    var smg = new SourceMapGenerator({
+      file: 'testing.js',
+      sourceRoot: '/wu/tang'
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 1, column: 0 },
+      generated: { line: 2, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 2, column: 0 },
+      generated: { line: 3, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 3, column: 0 },
+      generated: { line: 4, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 4, column: 0 },
+      generated: { line: 5, column: 2 }
+    });
+
+    var smc = new SourceMapConsumer(smg.toString());
+
+    // Exact
+    util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 0, null, smc, assert);
+    util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 0, null, smc, assert);
+    util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 0, null, smc, assert);
+    util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 0, null, smc, assert);
+
+    // Fuzzy
+    util.assertMapping(2, 0, null, null, null, null, smc, assert);
+    util.assertMapping(2, 9, '/wu/tang/gza.coffee', 1, 0, null, smc, assert);
+    util.assertMapping(3, 0, '/wu/tang/gza.coffee', 1, 0, null, smc, assert);
+    util.assertMapping(3, 9, '/wu/tang/gza.coffee', 2, 0, null, smc, assert);
+    util.assertMapping(4, 0, '/wu/tang/gza.coffee', 2, 0, null, smc, assert);
+    util.assertMapping(4, 9, '/wu/tang/gza.coffee', 3, 0, null, smc, assert);
+    util.assertMapping(5, 0, '/wu/tang/gza.coffee', 3, 0, null, smc, assert);
+    util.assertMapping(5, 9, '/wu/tang/gza.coffee', 4, 0, null, smc, assert);
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-dog-fooding', do_throw);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
@@ -0,0 +1,75 @@
+/*
+ * 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 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/test-source-map-consumer", ["require", "exports", "module"], function (require, exports, module) {
+
+  var SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
+
+  exports['test that we can instantiate with a string or an objects'] = function (assert, util) {
+    assert.doesNotThrow(function () {
+      var map = new SourceMapConsumer(util.testMap);
+    });
+    assert.doesNotThrow(function () {
+      var map = new SourceMapConsumer(JSON.stringify(util.testMap));
+    });
+  };
+
+  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
+    });
+    assert.equal(mapping.source, '/the/root/two.js');
+
+    mapping = map.originalPositionFor({
+      line: 1,
+      column: 1
+    });
+    assert.equal(mapping.source, '/the/root/one.js');
+  };
+
+  exports['test mapping tokens back exactly'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+
+    util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, map, assert);
+    util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, map, assert);
+    util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, map, assert);
+    util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', map, assert);
+    util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, map, assert);
+    util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', map, assert);
+    util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', map, assert);
+
+    util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, map, assert);
+    util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, map, assert);
+    util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, map, assert);
+    util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', map, assert);
+    util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, map, assert);
+    util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', map, assert);
+  };
+
+  exports['test mapping tokens back fuzzy'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+
+    util.assertMapping(1, 20, '/the/root/one.js', 1, 21, 'bar', map, assert);
+    util.assertMapping(1, 30, '/the/root/one.js', 2, 10, 'baz', map, assert);
+    util.assertMapping(2, 12, '/the/root/two.js', 1, 11, null, map, assert);
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-source-map-consumer', do_throw);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_map_generator.js
@@ -0,0 +1,187 @@
+/*
+ * 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 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/test-source-map-generator", ["require", "exports", "module"], function (require, exports, module) {
+
+  var SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
+
+  exports['test some simple stuff'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'foo.js',
+      sourceRoot: '.'
+    });
+    assert.ok(true);
+  };
+
+  exports['test adding mappings (case 1)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test adding mappings (case 2)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        source: 'bar.js',
+        original: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test adding mappings (case 3)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        source: 'bar.js',
+        original: { line: 1, column: 1 },
+        name: 'someToken'
+      });
+    });
+  };
+
+  exports['test adding mappings (invalid)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    // Not enough info.
+    assert.throws(function () {
+      map.addMapping({});
+    });
+
+    // Original file position, but no source.
+    assert.throws(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        original: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test that the correct mappings are being generated'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'min.js',
+      sourceRoot: '/the/root'
+    });
+
+    map.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 5 },
+      original: { line: 1, column: 5 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 9 },
+      original: { line: 1, column: 11 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 18 },
+      original: { line: 1, column: 21 },
+      source: 'one.js',
+      name: 'bar'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 21 },
+      original: { line: 2, column: 3 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 28 },
+      original: { line: 2, column: 10 },
+      source: 'one.js',
+      name: 'baz'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 32 },
+      original: { line: 2, column: 14 },
+      source: 'one.js',
+      name: 'bar'
+    });
+
+    map.addMapping({
+      generated: { line: 2, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 5 },
+      original: { line: 1, column: 5 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 9 },
+      original: { line: 1, column: 11 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 18 },
+      original: { line: 1, column: 21 },
+      source: 'two.js',
+      name: 'n'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 21 },
+      original: { line: 2, column: 3 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 28 },
+      original: { line: 2, column: 10 },
+      source: 'two.js',
+      name: 'n'
+    });
+
+    map = JSON.parse(map.toString());
+
+    assert.equal(map.version, 3);
+    assert.equal(map.file, 'min.js');
+    assert.equal(map.names.length, 3);
+    assert.equal(map.names[0], 'bar');
+    assert.equal(map.names[1], 'baz');
+    assert.equal(map.names[2], 'n');
+    assert.equal(map.sources.length, 2);
+    assert.equal(map.sources[0], 'one.js');
+    assert.equal(map.sources[1], 'two.js');
+    assert.equal(map.sourceRoot, '/the/root');
+    assert.equal(map.mappings, 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA');
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-source-map-generator', do_throw);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_node.js
@@ -0,0 +1,155 @@
+/*
+ * 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 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/test-source-node", ["require", "exports", "module"], function (require, exports, module) {
+
+  var SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
+  var SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
+  var SourceNode = require('source-map/source-node').SourceNode;
+
+  exports['test .add()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null);
+
+    // Adding a string works.
+    node.add('function noop() {}');
+
+    // Adding another source node works.
+    node.add(new SourceNode(null, null, null));
+
+    // Adding an array works.
+    node.add(['function foo() {',
+              new SourceNode(null, null, null,
+                             'return 10;'),
+              '}']);
+
+    // Adding other stuff doesn't.
+    assert.throws(function () {
+      node.add({});
+    });
+    assert.throws(function () {
+      node.add(function () {});
+    });
+  };
+
+  exports['test .prepend()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null);
+
+    // Prepending a string works.
+    node.prepend('function noop() {}');
+    assert.equal(node.children[0], 'function noop() {}');
+    assert.equal(node.children.length, 1);
+
+    // Prepending another source node works.
+    node.prepend(new SourceNode(null, null, null));
+    assert.equal(node.children[0], '');
+    assert.equal(node.children[1], 'function noop() {}');
+    assert.equal(node.children.length, 2);
+
+    // Prepending an array works.
+    node.prepend(['function foo() {',
+              new SourceNode(null, null, null,
+                             'return 10;'),
+              '}']);
+    assert.equal(node.children[0], 'function foo() {');
+    assert.equal(node.children[1], 'return 10;');
+    assert.equal(node.children[2], '}');
+    assert.equal(node.children[3], '');
+    assert.equal(node.children[4], 'function noop() {}');
+    assert.equal(node.children.length, 5);
+
+    // Prepending other stuff doesn't.
+    assert.throws(function () {
+      node.prepend({});
+    });
+    assert.throws(function () {
+      node.prepend(function () {});
+    });
+  };
+
+  exports['test .toString()'] = function (assert, util) {
+    assert.equal((new SourceNode(null, null, null,
+                                 ['function foo() {',
+                                  new SourceNode(null, null, null, 'return 10;'),
+                                  '}'])).toString(),
+                 'function foo() {return 10;}');
+  };
+
+  exports['test .join()'] = function (assert, util) {
+    assert.equal((new SourceNode(null, null, null,
+                                 ['a', 'b', 'c', 'd'])).join(', ').toString(),
+                 'a, b, c, d');
+  };
+
+  exports['test .walk()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ', new SourceNode(1, 0, 'a.js', ['someCall()']), ';\n',
+                               '  ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';\n',
+                               '}());']);
+    var expected = [
+      { str: '(function () {\n', source: null,   line: null, column: null },
+      { str: '  ',               source: null,   line: null, column: null },
+      { str: 'someCall()',       source: 'a.js', line: 1,    column: 0    },
+      { str: ';\n',              source: null,   line: null, column: null },
+      { str: '  ',               source: null,   line: null, column: null },
+      { str: 'if (foo) bar()',   source: 'b.js', line: 2,    column: 0    },
+      { str: ';\n',              source: null,   line: null, column: null },
+      { str: '}());',            source: null,   line: null, column: null },
+    ];
+    var i = 0;
+    node.walk(function (chunk, loc) {
+      assert.equal(expected[i].str, chunk);
+      assert.equal(expected[i].source, loc.source);
+      assert.equal(expected[i].line, loc.line);
+      assert.equal(expected[i].column, loc.column);
+      i++;
+    });
+  };
+
+  exports['test .toStringWithSourceMap()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ', new SourceNode(1, 0, 'a.js', ['someCall()']), ';\n',
+                               '  ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';\n',
+                               '}());']);
+    var map = node.toStringWithSourceMap({
+      file: 'foo.js'
+    }).map;
+
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = new SourceMapConsumer(map.toString());
+
+    var actual;
+
+    actual = map.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(actual.source, 'a.js');
+    assert.equal(actual.line, 1);
+    assert.equal(actual.column, 0);
+
+    actual = map.originalPositionFor({
+      line: 3,
+      column: 2
+    });
+    assert.equal(actual.source, 'b.js');
+    assert.equal(actual.line, 2);
+    assert.equal(actual.column, 0);
+  };
+
+});
+function run_test() {
+  runSourceMapTests('test/source-map/test-source-node', do_throw);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/sourcemap/tests/unit/xpcshell.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+head = head_sourcemap.js
+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]