Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 29 Apr 2013 13:09:28 -0400
changeset 130229 dd0c611a0a27f6e0fa5baae72ba4e1573fd64a63
parent 130228 b06a734d735b52e1f073acf0a3edb19c31a36eba (current diff)
parent 130183 059a39ad762c6eb727698f587c16074a24d4dfbd (diff)
child 130230 1eb382609c2d685779896f3168ef579907531abe
child 130242 cf9469c2c3c628e1327d8c27c611d71513c9f6cf
push id24605
push userkwierso@gmail.com
push dateMon, 29 Apr 2013 21:47:00 +0000
treeherdermozilla-central@dd0c611a0a27 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.0a1
first release with
nightly linux32
dd0c611a0a27 / 23.0a1 / 20130430030941 / files
nightly linux64
dd0c611a0a27 / 23.0a1 / 20130430030941 / files
nightly mac
dd0c611a0a27 / 23.0a1 / 20130430030941 / files
nightly win32
dd0c611a0a27 / 23.0a1 / 20130430030941 / files
nightly win64
dd0c611a0a27 / 23.0a1 / 20130430030941 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound.
browser/devtools/framework/Sidebar.jsm
browser/devtools/framework/Target.jsm
browser/devtools/framework/ToolDefinitions.jsm
browser/devtools/framework/Toolbox.jsm
browser/devtools/framework/ToolboxHosts.jsm
browser/devtools/inspector/Breadcrumbs.jsm
browser/devtools/inspector/Highlighter.jsm
browser/devtools/inspector/InspectorPanel.jsm
browser/devtools/inspector/Selection.jsm
browser/devtools/inspector/SelectorSearch.jsm
browser/devtools/markupview/MarkupView.jsm
browser/devtools/shared/EventEmitter.jsm
browser/devtools/shared/InplaceEditor.jsm
browser/devtools/shared/Undo.jsm
browser/devtools/styleinspector/CssHtmlTree.jsm
browser/devtools/styleinspector/CssLogic.jsm
browser/devtools/styleinspector/CssRuleView.jsm
browser/devtools/styleinspector/StyleInspector.jsm
browser/devtools/tilt/Tilt.jsm
browser/devtools/tilt/TiltGL.jsm
browser/devtools/tilt/TiltMath.jsm
browser/devtools/tilt/TiltUtils.jsm
browser/devtools/tilt/TiltVisualizer.jsm
browser/devtools/tilt/TiltVisualizerStyle.jsm
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -432,20 +432,19 @@ nsContextMenu.prototype = {
 
   initClickToPlayItems: function() {
     this.showItem("context-ctp-play", this.onCTPPlugin);
     this.showItem("context-ctp-hide", this.onCTPPlugin);
     this.showItem("context-sep-ctp", this.onCTPPlugin);
   },
 
   inspectNode: function CM_inspectNode() {
+    let {devtools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
     let gBrowser = this.browser.ownerDocument.defaultView.gBrowser;
-    let imported = {};
-    Cu.import("resource:///modules/devtools/Target.jsm", imported);
-    let tt = imported.TargetFactory.forTab(gBrowser.selectedTab);
+    let tt = devtools.TargetFactory.forTab(gBrowser.selectedTab);
     return gDevTools.showToolbox(tt, "inspector").then(function(toolbox) {
       let inspector = toolbox.getCurrentPanel();
       inspector.selection.setNode(this.target, "browser-context-menu");
     }.bind(this));
   },
 
   // Set various context menu attributes based on the state of the world.
   setTarget: function (aNode, aRangeParent, aRangeOffset) {
--- a/browser/components/sessionstore/test/browser_pageshow.js
+++ b/browser/components/sessionstore/test/browser_pageshow.js
@@ -10,37 +10,40 @@ function test() {
  * in history) marks the window as dirty and causes data about the tab that
  * changed to be re-collected.
  *
  * We will do this by creating a tab with two history entries and going back
  * to the first. When we now request the current browser state from the
  * session store service the first history entry must be selected.
  */
 
+const URL = "data:text/html,<h1>first</h1>";
+const URL2 = "data:text/html,<h1>second</h1>";
+
 function runTests() {
   // Create a dummy window that is regarded as active. We need to do this
   // because we always collect data for tabs of active windows no matter if
   // the window is dirty or not.
   let win = OpenBrowserWindow();
   yield waitForLoad(win);
 
   // Create a tab with two history entries.
   let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
-  yield loadURI("about:robots");
-  yield loadURI("about:mozilla");
+  yield loadURI(URL);
+  yield loadURI(URL2);
 
   // All windows currently marked as dirty will be written to disk
   // and thus marked clean afterwards.
   yield forceWriteState();
 
   // Go back to 'about:robots' - which is loaded from the bfcache and thus
   // will not fire a 'load' event but a 'pageshow' event with persisted=true.
   waitForPageShow();
   yield gBrowser.selectedBrowser.goBack();
-  is(tab.linkedBrowser.currentURI.spec, "about:robots", "url is about:robots");
+  is(tab.linkedBrowser.currentURI.spec, URL, "correct url after going back");
 
   // If by receiving the 'pageshow' event the first window has correctly
   // been marked as dirty, getBrowserState() should return the tab we created
   // with the right history entry (about:robots) selected.
   let state = JSON.parse(ss.getBrowserState());
   is(state.windows[0].tabs[1].index, 1, "first history entry is selected");
 
   // Clean up after ourselves.
new file mode 100644
--- /dev/null
+++ b/browser/devtools/Makefile.in
@@ -0,0 +1,15 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH     = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(topsrcdir)/config/config.mk
+
+include $(topsrcdir)/config/rules.mk
+
+libs::
+	$(NSINSTALL) $(srcdir)/main.js $(FINAL_TARGET)/modules/devtools
--- a/browser/devtools/commandline/BuiltinCommands.jsm
+++ b/browser/devtools/commandline/BuiltinCommands.jsm
@@ -9,23 +9,23 @@ const BRAND_SHORT_NAME = Cc["@mozilla.or
                            .createBundle("chrome://branding/locale/brand.properties")
                            .GetStringFromName("brandShortName");
 
 this.EXPORTED_SYMBOLS = [ "CmdAddonFlags", "CmdCommands" ];
 
 Cu.import("resource:///modules/devtools/gcli.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/osfile.jsm")
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource:///modules/devtools/shared/event-emitter.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
                                   "resource:///modules/devtools/gDevTools.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
-                                  "resource:///modules/devtools/Target.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "devtools",
+                                  "resource:///modules/devtools/gDevTools.jsm");
 
 /* CmdAddon ---------------------------------------------------------------- */
 
 (function(module) {
   XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                     "resource://gre/modules/AddonManager.jsm");
 
   // We need to use an object in which to store any flags because a primitive
@@ -400,19 +400,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
     Components.utils.import("resource://gre/modules/jsdebugger.jsm", JsDebugger);
 
     let global = Components.utils.getGlobalForObject({});
     JsDebugger.addDebuggerToGlobal(global);
 
     return global.Debugger;
   });
 
-  XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
-                                    "resource:///modules/devtools/Target.jsm");
-
   let debuggers = [];
 
   /**
   * 'calllog' command
   */
   gcli.addCommand({
     name: "calllog",
     description: gcli.lookup("calllogDesc")
@@ -432,17 +429,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
       dbg.onEnterFrame = function(frame) {
         // BUG 773652 -  Make the output from the GCLI calllog command nicer
         contentWindow.console.log("Method call: " + this.callDescription(frame));
       }.bind(this);
 
       debuggers.push(dbg);
 
       let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-      let target = TargetFactory.forTab(gBrowser.selectedTab);
+      let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
       gDevTools.showToolbox(target, "webconsole");
 
       return gcli.lookup("calllogStartReply");
     },
 
     callDescription: function(frame) {
       let name = "<anonymous>";
       if (frame.callee.name) {
@@ -584,17 +581,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
       dbg.onEnterFrame = function(frame) {
         // BUG 773652 -  Make the output from the GCLI calllog command nicer
         contentWindow.console.log(gcli.lookup("callLogChromeMethodCall") +
                                   ": " + this.callDescription(frame));
       }.bind(this);
 
       let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-      let target = TargetFactory.forTab(gBrowser.selectedTab);
+      let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
       gDevTools.showToolbox(target, "webconsole");
 
       return gcli.lookup("calllogChromeStartReply");
     },
 
     valueToString: function(value) {
       if (typeof value !== "object" || value === null)
         return uneval(value);
@@ -830,30 +827,30 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   /**
   * 'console close' command
   */
   gcli.addCommand({
     name: "console close",
     description: gcli.lookup("consolecloseDesc"),
     exec: function Command_consoleClose(args, context) {
       let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-      let target = TargetFactory.forTab(gBrowser.selectedTab);
+      let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
       return gDevTools.closeToolbox(target);
     }
   });
 
   /**
   * 'console open' command
   */
   gcli.addCommand({
     name: "console open",
     description: gcli.lookup("consoleopenDesc"),
     exec: function Command_consoleOpen(args, context) {
       let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-      let target = TargetFactory.forTab(gBrowser.selectedTab);
+      let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
       return gDevTools.showToolbox(target, "webconsole");
     }
   });
 }(this));
 
 /* CmdCookie --------------------------------------------------------------- */
 
 (function(module) {
@@ -1594,16 +1591,85 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   * @return string
   *         The equivalent of |aString| but safe to use in a regex.
   */
   function escapeRegex(aString) {
     return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
   }
 }(this));
 
+/* CmdTools -------------------------------------------------------------- */
+
+(function(module) {
+  gcli.addCommand({
+    name: "tools",
+    description: gcli.lookup("toolsDesc"),
+    manual: gcli.lookup("toolsManual"),
+    get hidden() gcli.hiddenByChromePref(),
+  });
+
+  gcli.addCommand({
+    name: "tools srcdir",
+    description: gcli.lookup("toolsSrcdirDesc"),
+    manual: gcli.lookup("toolsSrcdirManual"),
+    get hidden() gcli.hiddenByChromePref(),
+    params: [
+      {
+        name: "srcdir",
+        type: "string",
+        description: gcli.lookup("toolsSrcdirDir")
+      }
+    ],
+    returnType: "string",
+    exec: function(args, context) {
+      let promise = context.createPromise();
+      let existsPromise = OS.File.exists(args.srcdir + "/CLOBBER");
+      existsPromise.then(function(exists) {
+        if (exists) {
+          var str = Cc["@mozilla.org/supports-string;1"]
+            .createInstance(Ci.nsISupportsString);
+          str.data = args.srcdir;
+          Services.prefs.setComplexValue("devtools.loader.srcdir",
+              Components.interfaces.nsISupportsString, str);
+          devtools.reload();
+          promise.resolve(gcli.lookupFormat("toolsSrcdirReloaded", [args.srcdir]));
+          return;
+        }
+        promise.reject(gcli.lookupFormat("toolsSrcdirNotFound", [args.srcdir]));
+      });
+      return promise;
+    }
+  });
+
+  gcli.addCommand({
+    name: "tools builtin",
+    description: gcli.lookup("toolsBuiltinDesc"),
+    manual: gcli.lookup("toolsBuiltinManual"),
+    get hidden() gcli.hiddenByChromePref(),
+    returnType: "string",
+    exec: function(args, context) {
+      Services.prefs.clearUserPref("devtools.loader.srcdir");
+      devtools.reload();
+      return gcli.lookup("toolsBuiltinReloaded");
+    }
+  });
+
+  gcli.addCommand({
+    name: "tools reload",
+    description: gcli.lookup("toolsReloadDesc"),
+    get hidden() gcli.hiddenByChromePref() || !Services.prefs.prefHasUserValue("devtools.loader.srcdir"),
+
+    returnType: "string",
+    exec: function(args, context) {
+      devtools.reload();
+      return gcli.lookup("toolsReloaded");
+    }
+  });
+}(this));
+
 /* CmdRestart -------------------------------------------------------------- */
 
 (function(module) {
   /**
   * Restart command
   *
   * @param boolean nocache
   *        Disables loading content from cache upon restart.
@@ -1988,13 +2054,13 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   let eventEmitter = new EventEmitter();
   function onPaintFlashingChanged(context) {
     var gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
     var tab = gBrowser.selectedTab;
     eventEmitter.emit("changed", tab);
     function fireChange() {
       eventEmitter.emit("changed", tab);
     }
-    var target = TargetFactory.forTab(tab);
+    var target = devtools.TargetFactory.forTab(tab);
     target.off("navigate", fireChange);
     target.once("navigate", fireChange);
   }
 }(this));
--- a/browser/devtools/commandline/test/helpers.js
+++ b/browser/devtools/commandline/test/helpers.js
@@ -19,17 +19,18 @@
 
 this.EXPORTED_SYMBOLS = [ 'helpers' ];
 var helpers = {};
 this.helpers = helpers;
 let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require;
 Components.utils.import("resource:///modules/devtools/gcli.jsm", {});
 
 let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
-let TargetFactory = (Cu.import("resource:///modules/devtools/Target.jsm", {})).TargetFactory;
+let devtools = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools;
+let TargetFactory = devtools.TargetFactory;
 
 let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
 let assert = { ok: ok, is: is, log: info };
 
 var util = require('util/util');
 
 var converters = require('gcli/converters');
 
--- a/browser/devtools/debugger/DebuggerPanel.jsm
+++ b/browser/devtools/debugger/DebuggerPanel.jsm
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 this.EXPORTED_SYMBOLS = ["DebuggerPanel"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
+Cu.import("resource:///modules/devtools/shared/event-emitter.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/commonjs/sdk/core/promise.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
   "resource://gre/modules/devtools/dbg-server.jsm");
 
 function DebuggerPanel(iframeWindow, toolbox) {
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -7,24 +7,24 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 let tempScope = {};
 Cu.import("resource://gre/modules/Services.jsm", tempScope);
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm", tempScope);
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm", tempScope);
 Cu.import("resource:///modules/source-editor.jsm", tempScope);
 Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
 let Services = tempScope.Services;
 let SourceEditor = tempScope.SourceEditor;
 let DebuggerServer = tempScope.DebuggerServer;
 let DebuggerTransport = tempScope.DebuggerTransport;
 let DebuggerClient = tempScope.DebuggerClient;
 let gDevTools = tempScope.gDevTools;
-let TargetFactory = tempScope.TargetFactory;
+let devtools = tempScope.devtools;
+let TargetFactory = devtools.TargetFactory;
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
 const TAB1_URL = EXAMPLE_URL + "browser_dbg_tab1.html";
 const TAB2_URL = EXAMPLE_URL + "browser_dbg_tab2.html";
--- a/browser/devtools/debugger/test/helpers.js
+++ b/browser/devtools/debugger/test/helpers.js
@@ -19,17 +19,18 @@
 
 this.EXPORTED_SYMBOLS = [ 'helpers' ];
 var helpers = {};
 this.helpers = helpers;
 let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require;
 Components.utils.import("resource:///modules/devtools/gcli.jsm", {});
 
 let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
-let TargetFactory = (Cu.import("resource:///modules/devtools/Target.jsm", {})).TargetFactory;
+let devtools = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools;
+let TargetFactory = devtools.TargetFactory;
 
 let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
 let assert = { ok: ok, is: is, log: info };
 
 var util = require('util/util');
 
 var converters = require('gcli/converters');
 
--- a/browser/devtools/fontinspector/test/browser_fontinspector.js
+++ b/browser/devtools/fontinspector/test/browser_fontinspector.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
+let {devtools, gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
+let TargetFactory = devtools.TargetFactory;
 
 let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 
 function test() {
   waitForExplicitFinish();
 
   let doc;
   let node;
--- a/browser/devtools/framework/Makefile.in
+++ b/browser/devtools/framework/Makefile.in
@@ -8,8 +8,9 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
+	$(NSINSTALL) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools/framework
deleted file mode 100644
--- a/browser/devtools/framework/Sidebar.jsm
+++ /dev/null
@@ -1,213 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-this.EXPORTED_SYMBOLS = ["ToolSidebar"];
-
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-
-const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-/**
- * ToolSidebar provides methods to register tabs in the sidebar.
- * It's assumed that the sidebar contains a xul:tabbox.
- *
- * @param {Node} tabbox
- *  <tabbox> node;
- * @param {ToolPanel} panel
- *  Related ToolPanel instance;
- * @param {Boolean} showTabstripe
- *  Show the tabs.
- */
-this.ToolSidebar = function ToolSidebar(tabbox, panel, showTabstripe=true)
-{
-  EventEmitter.decorate(this);
-
-  this._tabbox = tabbox;
-  this._panelDoc = this._tabbox.ownerDocument;
-  this._toolPanel = panel;
-
-  this._tabbox.tabpanels.addEventListener("select", this, true);
-
-  this._tabs = new Map();
-
-  if (!showTabstripe) {
-    this._tabbox.setAttribute("hidetabs", "true");
-  }
-}
-
-ToolSidebar.prototype = {
-  /**
-   * Register a tab. A tab is a document.
-   * The document must have a title, which will be used as the name of the tab.
-   *
-   * @param {string} tab uniq id
-   * @param {string} url
-   */
-  addTab: function ToolSidebar_addTab(id, url, selected=false) {
-    let iframe = this._panelDoc.createElementNS(XULNS, "iframe");
-    iframe.className = "iframe-" + id;
-    iframe.setAttribute("flex", "1");
-    iframe.setAttribute("src", url);
-    iframe.tooltip = "aHTMLTooltip";
-
-    let tab = this._tabbox.tabs.appendItem();
-    tab.setAttribute("label", ""); // Avoid showing "undefined" while the tab is loading
-
-    let onIFrameLoaded = function() {
-      tab.setAttribute("label", iframe.contentDocument.title);
-      iframe.removeEventListener("load", onIFrameLoaded, true);
-      if ("setPanel" in iframe.contentWindow) {
-        iframe.contentWindow.setPanel(this._toolPanel, iframe);
-      }
-      this.emit(id + "-ready");
-    }.bind(this);
-
-    iframe.addEventListener("load", onIFrameLoaded, true);
-
-    let tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel");
-    tabpanel.setAttribute("id", "sidebar-panel-" + id);
-    tabpanel.appendChild(iframe);
-    this._tabbox.tabpanels.appendChild(tabpanel);
-
-    this._tooltip = this._panelDoc.createElementNS(XULNS, "tooltip");
-    this._tooltip.id = "aHTMLTooltip";
-    tabpanel.appendChild(this._tooltip);
-    this._tooltip.page = true;
-
-    tab.linkedPanel = "sidebar-panel-" + id;
-
-    // We store the index of this tab.
-    this._tabs.set(id, tab);
-
-    if (selected) {
-      // For some reason I don't understand, if we call this.select in this
-      // event loop (after inserting the tab), the tab will never get the
-      // the "selected" attribute set to true.
-      this._panelDoc.defaultView.setTimeout(function() {
-        this.select(id);
-      }.bind(this), 10);
-    }
-
-    this.emit("new-tab-registered", id);
-  },
-
-  /**
-   * Select a specific tab.
-   */
-  select: function ToolSidebar_select(id) {
-    let tab = this._tabs.get(id);
-    if (tab) {
-      this._tabbox.selectedTab = tab;
-    }
-  },
-
-  /**
-   * Return the id of the selected tab.
-   */
-  getCurrentTabID: function ToolSidebar_getCurrentTabID() {
-    let currentID = null;
-    for (let [id, tab] of this._tabs) {
-      if (this._tabbox.tabs.selectedItem == tab) {
-        currentID = id;
-        break;
-      }
-    }
-    return currentID;
-  },
-
-  /**
-   * Returns the requested tab based on the id.
-   *
-   * @param String id
-   *        unique id of the requested tab.
-   */
-  getTab: function ToolSidebar_getTab(id) {
-    return this._tabbox.tabpanels.querySelector("#sidebar-panel-" + id);
-  },
-
-  /**
-   * Event handler.
-   */
-  handleEvent: function ToolSidebar_eventHandler(event) {
-    if (event.type == "select") {
-      let previousTool = this._currentTool;
-      this._currentTool = this.getCurrentTabID();
-      if (previousTool) {
-        this.emit(previousTool + "-unselected");
-      }
-
-      this.emit(this._currentTool + "-selected");
-      this.emit("select", this._currentTool);
-    }
-  },
-
-  /**
-   * Toggle sidebar's visibility state.
-   */
-  toggle: function ToolSidebar_toggle() {
-    if (this._tabbox.hasAttribute("hidden")) {
-      this.show();
-    } else {
-      this.hide();
-    }
-  },
-
-  /**
-   * Show the sidebar.
-   */
-  show: function ToolSidebar_show() {
-    this._tabbox.removeAttribute("hidden");
-  },
-
-  /**
-   * Show the sidebar.
-   */
-  hide: function ToolSidebar_hide() {
-    this._tabbox.setAttribute("hidden", "true");
-  },
-
-  /**
-   * Return the window containing the tab content.
-   */
-  getWindowForTab: function ToolSidebar_getWindowForTab(id) {
-    if (!this._tabs.has(id)) {
-      return null;
-    }
-
-    let panel = this._panelDoc.getElementById(this._tabs.get(id).linkedPanel);
-    return panel.firstChild.contentWindow;
-  },
-
-  /**
-   * Clean-up.
-   */
-  destroy: function ToolSidebar_destroy() {
-    if (this._destroyed) {
-      return Promise.resolve(null);
-    }
-    this._destroyed = true;
-
-    this._tabbox.tabpanels.removeEventListener("select", this, true);
-
-    while (this._tabbox.tabpanels.hasChildNodes()) {
-      this._tabbox.tabpanels.removeChild(this._tabbox.tabpanels.firstChild);
-    }
-
-    while (this._tabbox.tabs.hasChildNodes()) {
-      this._tabbox.tabs.removeChild(this._tabbox.tabs.firstChild);
-    }
-
-    this._tabs = null;
-    this._tabbox = null;
-    this._panelDoc = null;
-    this._toolPanel = null;
-
-    return Promise.resolve(null);
-  },
-}
deleted file mode 100644
--- a/browser/devtools/framework/Target.jsm
+++ /dev/null
@@ -1,598 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = [ "TargetFactory" ];
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
-  "resource://gre/modules/devtools/dbg-server.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
-  "resource://gre/modules/devtools/dbg-client.jsm");
-
-const targets = new WeakMap();
-const promiseTargets = new WeakMap();
-
-/**
- * Functions for creating Targets
- */
-this.TargetFactory = {
-  /**
-   * Construct a Target
-   * @param {XULTab} tab
-   *        The tab to use in creating a new target.
-   *
-   * @return A target object
-   */
-  forTab: function TF_forTab(tab) {
-    let target = targets.get(tab);
-    if (target == null) {
-      target = new TabTarget(tab);
-      targets.set(tab, target);
-    }
-    return target;
-  },
-
-  /**
-   * Return a promise of a Target for a remote tab.
-   * @param {Object} options
-   *        The options object has the following properties:
-   *        {
-   *          form: the remote protocol form of a tab,
-   *          client: a DebuggerClient instance,
-   *          chrome: true if the remote target is the whole process
-   *        }
-   *
-   * @return A promise of a target object
-   */
-  forRemoteTab: function TF_forRemoteTab(options) {
-    let promise = promiseTargets.get(options);
-    if (promise == null) {
-      let target = new TabTarget(options);
-      promise = target.makeRemote().then(() => target);
-      promiseTargets.set(options, promise);
-    }
-    return promise;
-  },
-
-  /**
-   * Creating a target for a tab that is being closed is a problem because it
-   * allows a leak as a result of coming after the close event which normally
-   * clears things up. This function allows us to ask if there is a known
-   * target for a tab without creating a target
-   * @return true/false
-   */
-  isKnownTab: function TF_isKnownTab(tab) {
-    return targets.has(tab);
-  },
-
-  /**
-   * Construct a Target
-   * @param {nsIDOMWindow} window
-   *        The chromeWindow to use in creating a new target
-   * @return A target object
-   */
-  forWindow: function TF_forWindow(window) {
-    let target = targets.get(window);
-    if (target == null) {
-      target = new WindowTarget(window);
-      targets.set(window, target);
-    }
-    return target;
-  },
-
-  /**
-   * Get all of the targets known to the local browser instance
-   * @return An array of target objects
-   */
-  allTargets: function TF_allTargets() {
-    let windows = [];
-    let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                       .getService(Components.interfaces.nsIWindowMediator);
-    let en = wm.getXULWindowEnumerator(null);
-    while (en.hasMoreElements()) {
-      windows.push(en.getNext());
-    }
-
-    return windows.map(function(window) {
-      return TargetFactory.forWindow(window);
-    });
-  },
-};
-
-/**
- * The 'version' property allows the developer tools equivalent of browser
- * detection. Browser detection is evil, however while we don't know what we
- * will need to detect in the future, it is an easy way to postpone work.
- * We should be looking to use 'supports()' in place of version where
- * possible.
- */
-function getVersion() {
-  // FIXME: return something better
-  return 20;
-}
-
-/**
- * A better way to support feature detection, but we're not yet at a place
- * where we have the features well enough defined for this to make lots of
- * sense.
- */
-function supports(feature) {
-  // FIXME: return something better
-  return false;
-};
-
-/**
- * A Target represents something that we can debug. Targets are generally
- * read-only. Any changes that you wish to make to a target should be done via
- * a Tool that attaches to the target. i.e. a Target is just a pointer saying
- * "the thing to debug is over there".
- *
- * Providing a generalized abstraction of a web-page or web-browser (available
- * either locally or remotely) is beyond the scope of this class (and maybe
- * also beyond the scope of this universe) However Target does attempt to
- * abstract some common events and read-only properties common to many Tools.
- *
- * Supported read-only properties:
- * - name, isRemote, url
- *
- * Target extends EventEmitter and provides support for the following events:
- * - close: The target window has been closed. All tools attached to this
- *     target should close. This event is not currently cancelable.
- * - navigate: The target window has navigated to a different URL
- *
- * Optional events:
- * - will-navigate: The target window will navigate to a different URL
- * - hidden: The target is not visible anymore (for TargetTab, another tab is selected)
- * - visible: The target is visible (for TargetTab, tab is selected)
- *
- * Target also supports 2 functions to help allow 2 different versions of
- * Firefox debug each other. The 'version' property is the equivalent of
- * browser detection - simple and easy to implement but gets fragile when things
- * are not quite what they seem. The 'supports' property is the equivalent of
- * feature detection - harder to setup, but more robust long-term.
- *
- * Comparing Targets: 2 instances of a Target object can point at the same
- * thing, so t1 !== t2 and t1 != t2 even when they represent the same object.
- * To compare to targets use 't1.equals(t2)'.
- */
-function Target() {
-  throw new Error("Use TargetFactory.newXXX or Target.getXXX to create a Target in place of 'new Target()'");
-}
-
-Object.defineProperty(Target.prototype, "version", {
-  get: getVersion,
-  enumerable: true
-});
-
-
-/**
- * A TabTarget represents a page living in a browser tab. Generally these will
- * be web pages served over http(s), but they don't have to be.
- */
-function TabTarget(tab) {
-  EventEmitter.decorate(this);
-  this.destroy = this.destroy.bind(this);
-  this._handleThreadState = this._handleThreadState.bind(this);
-  this.on("thread-resumed", this._handleThreadState);
-  this.on("thread-paused", this._handleThreadState);
-  // Only real tabs need initialization here. Placeholder objects for remote
-  // targets will be initialized after a makeRemote method call.
-  if (tab && !["client", "form", "chrome"].every(tab.hasOwnProperty, tab)) {
-    this._tab = tab;
-    this._setupListeners();
-  } else {
-    this._form = tab.form;
-    this._client = tab.client;
-    this._chrome = tab.chrome;
-  }
-}
-
-TabTarget.prototype = {
-  _webProgressListener: null,
-
-  supports: supports,
-  get version() { return getVersion(); },
-
-  get tab() {
-    return this._tab;
-  },
-
-  get form() {
-    return this._form;
-  },
-
-  get client() {
-    return this._client;
-  },
-
-  get chrome() {
-    return this._chrome;
-  },
-
-  get window() {
-    // Be extra careful here, since this may be called by HS_getHudByWindow
-    // during shutdown.
-    if (this._tab && this._tab.linkedBrowser) {
-      return this._tab.linkedBrowser.contentWindow;
-    }
-  },
-
-  get name() {
-    return this._tab ? this._tab.linkedBrowser.contentDocument.title :
-                       this._form.title;
-  },
-
-  get url() {
-    return this._tab ? this._tab.linkedBrowser.contentDocument.location.href :
-                       this._form.url;
-  },
-
-  get isRemote() {
-    return !this.isLocalTab;
-  },
-
-  get isLocalTab() {
-    return !!this._tab;
-  },
-
-  get isThreadPaused() {
-    return !!this._isThreadPaused;
-  },
-
-  /**
-   * Adds remote protocol capabilities to the target, so that it can be used
-   * for tools that support the Remote Debugging Protocol even for local
-   * connections.
-   */
-  makeRemote: function TabTarget_makeRemote() {
-    if (this._remote) {
-      return this._remote.promise;
-    }
-
-    this._remote = Promise.defer();
-
-    if (this.isLocalTab) {
-      // Since a remote protocol connection will be made, let's start the
-      // DebuggerServer here, once and for all tools.
-      if (!DebuggerServer.initialized) {
-        DebuggerServer.init();
-        DebuggerServer.addBrowserActors();
-      }
-
-      this._client = new DebuggerClient(DebuggerServer.connectPipe());
-      // A local TabTarget will never perform chrome debugging.
-      this._chrome = false;
-    }
-
-    this._setupRemoteListeners();
-
-    let attachTab = () => {
-      this._client.attachTab(this._form.actor, (aResponse, aTabClient) => {
-        if (!aTabClient) {
-          this._remote.reject("Unable to attach to the tab");
-          return;
-        }
-        this.threadActor = aResponse.threadActor;
-        this._remote.resolve(null);
-      });
-    };
-
-    if (this.isLocalTab) {
-      this._client.connect((aType, aTraits) => {
-        this._client.listTabs(aResponse => {
-          this._form = aResponse.tabs[aResponse.selected];
-          attachTab();
-        });
-      });
-    } else if (!this.chrome) {
-      // In the remote debugging case, the protocol connection will have been
-      // already initialized in the connection screen code.
-      attachTab();
-    } else {
-      // Remote chrome debugging doesn't need anything at this point.
-      this._remote.resolve(null);
-    }
-
-    return this._remote.promise;
-  },
-
-  /**
-   * Listen to the different events.
-   */
-  _setupListeners: function TabTarget__setupListeners() {
-    this._webProgressListener = new TabWebProgressListener(this);
-    this.tab.linkedBrowser.addProgressListener(this._webProgressListener);
-    this.tab.addEventListener("TabClose", this);
-    this.tab.parentNode.addEventListener("TabSelect", this);
-    this.tab.ownerDocument.defaultView.addEventListener("unload", this);
-  },
-
-  /**
-   * Setup listeners for remote debugging, updating existing ones as necessary.
-   */
-  _setupRemoteListeners: function TabTarget__setupRemoteListeners() {
-    this.client.addListener("tabDetached", this.destroy);
-
-    this._onTabNavigated = function onRemoteTabNavigated(aType, aPacket) {
-      let event = Object.create(null);
-      event.url = aPacket.url;
-      event.title = aPacket.title;
-      event.nativeConsoleAPI = aPacket.nativeConsoleAPI;
-      // Send any stored event payload (DOMWindow or nsIRequest) for backwards
-      // compatibility with non-remotable tools.
-      if (aPacket.state == "start") {
-        event._navPayload = this._navRequest;
-        this.emit("will-navigate", event);
-        this._navRequest = null;
-      } else {
-        event._navPayload = this._navWindow;
-        this.emit("navigate", event);
-        this._navWindow = null;
-      }
-    }.bind(this);
-    this.client.addListener("tabNavigated", this._onTabNavigated);
-  },
-
-  /**
-   * Handle tabs events.
-   */
-  handleEvent: function (event) {
-    switch (event.type) {
-      case "TabClose":
-      case "unload":
-        this.destroy();
-        break;
-      case "TabSelect":
-        if (this.tab.selected) {
-          this.emit("visible", event);
-        } else {
-          this.emit("hidden", event);
-        }
-        break;
-    }
-  },
-
-  /**
-   * Handle script status.
-   */
-  _handleThreadState: function(event) {
-    switch (event) {
-      case "thread-resumed":
-        this._isThreadPaused = false;
-        break;
-      case "thread-paused":
-        this._isThreadPaused = true;
-        break;
-    }
-  },
-
-  /**
-   * Target is not alive anymore.
-   */
-  destroy: function() {
-    // If several things call destroy then we give them all the same
-    // destruction promise so we're sure to destroy only once
-    if (this._destroyer) {
-      return this._destroyer.promise;
-    }
-
-    this._destroyer = Promise.defer();
-
-    // Before taking any action, notify listeners that destruction is imminent.
-    this.emit("close");
-
-    // First of all, do cleanup tasks that pertain to both remoted and
-    // non-remoted targets.
-    this.off("thread-resumed", this._handleThreadState);
-    this.off("thread-paused", this._handleThreadState);
-
-    if (this._tab) {
-      if (this._webProgressListener) {
-        this._webProgressListener.destroy();
-      }
-
-      this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
-      this._tab.removeEventListener("TabClose", this);
-      this._tab.parentNode.removeEventListener("TabSelect", this);
-    }
-
-    // If this target was not remoted, the promise will be resolved before the
-    // function returns.
-    if (this._tab && !this._client) {
-      targets.delete(this._tab);
-      this._tab = null;
-      this._client = null;
-      this._form = null;
-      this._remote = null;
-
-      this._destroyer.resolve(null);
-    } else if (this._client) {
-      // If, on the other hand, this target was remoted, the promise will be
-      // resolved after the remote connection is closed.
-      this.client.removeListener("tabNavigated", this._onTabNavigated);
-      this.client.removeListener("tabDetached", this.destroy);
-
-      this._client.close(function onClosed() {
-        if (this._tab) {
-          targets.delete(this._tab);
-        } else {
-          promiseTargets.delete(this._form);
-        }
-        this._client = null;
-        this._tab = null;
-        this._form = null;
-        this._remote = null;
-
-        this._destroyer.resolve(null);
-      }.bind(this));
-    }
-
-    return this._destroyer.promise;
-  },
-
-  toString: function() {
-    return 'TabTarget:' + (this._tab ? this._tab : (this._form && this._form.actor));
-  },
-};
-
-
-/**
- * WebProgressListener for TabTarget.
- *
- * @param object aTarget
- *        The TabTarget instance to work with.
- */
-function TabWebProgressListener(aTarget) {
-  this.target = aTarget;
-}
-
-TabWebProgressListener.prototype = {
-  target: null,
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
-
-  onStateChange: function TWPL_onStateChange(progress, request, flag, status) {
-    let isStart = flag & Ci.nsIWebProgressListener.STATE_START;
-    let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
-    let isNetwork = flag & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
-    let isRequest = flag & Ci.nsIWebProgressListener.STATE_IS_REQUEST;
-
-    // Skip non-interesting states.
-    if (!isStart || !isDocument || !isRequest || !isNetwork) {
-      return;
-    }
-
-    // emit event if the top frame is navigating
-    if (this.target && this.target.window == progress.DOMWindow) {
-      // Emit the event if the target is not remoted or store the payload for
-      // later emission otherwise.
-      if (this.target._client) {
-        this.target._navRequest = request;
-      } else {
-        this.target.emit("will-navigate", request);
-      }
-    }
-  },
-
-  onProgressChange: function() {},
-  onSecurityChange: function() {},
-  onStatusChange: function() {},
-
-  onLocationChange: function TWPL_onLocationChange(webProgress, request, URI, flags) {
-    if (this.target &&
-        !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
-      let window = webProgress.DOMWindow;
-      // Emit the event if the target is not remoted or store the payload for
-      // later emission otherwise.
-      if (this.target._client) {
-        this.target._navWindow = window;
-      } else {
-        this.target.emit("navigate", window);
-      }
-    }
-  },
-
-  /**
-   * Destroy the progress listener instance.
-   */
-  destroy: function TWPL_destroy() {
-    if (this.target.tab) {
-      this.target.tab.linkedBrowser.removeProgressListener(this);
-    }
-    this.target._webProgressListener = null;
-    this.target._navRequest = null;
-    this.target._navWindow = null;
-    this.target = null;
-  }
-};
-
-
-/**
- * A WindowTarget represents a page living in a xul window or panel. Generally
- * these will have a chrome: URL
- */
-function WindowTarget(window) {
-  EventEmitter.decorate(this);
-  this._window = window;
-  this._setupListeners();
-}
-
-WindowTarget.prototype = {
-  supports: supports,
-  get version() { return getVersion(); },
-
-  get window() {
-    return this._window;
-  },
-
-  get name() {
-    return this._window.document.title;
-  },
-
-  get url() {
-    return this._window.document.location.href;
-  },
-
-  get isRemote() {
-    return false;
-  },
-
-  get isLocalTab() {
-    return false;
-  },
-
-  get isThreadPaused() {
-    return !!this._isThreadPaused;
-  },
-
-  /**
-   * Listen to the different events.
-   */
-  _setupListeners: function() {
-    this._handleThreadState = this._handleThreadState.bind(this);
-    this.on("thread-paused", this._handleThreadState);
-    this.on("thread-resumed", this._handleThreadState);
-  },
-
-  _handleThreadState: function(event) {
-    switch (event) {
-      case "thread-resumed":
-        this._isThreadPaused = false;
-        break;
-      case "thread-paused":
-        this._isThreadPaused = true;
-        break;
-    }
-  },
-
-  /**
-   * Target is not alive anymore.
-   */
-  destroy: function() {
-    if (!this._destroyed) {
-      this._destroyed = true;
-
-      this.off("thread-paused", this._handleThreadState);
-      this.off("thread-resumed", this._handleThreadState);
-      this.emit("close");
-
-      targets.delete(this._window);
-      this._window = null;
-    }
-
-    return Promise.resolve(null);
-  },
-
-  toString: function() {
-    return 'WindowTarget:' + this.window;
-  },
-};
deleted file mode 100644
--- a/browser/devtools/framework/ToolDefinitions.jsm
+++ /dev/null
@@ -1,225 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = [
-                          "defaultTools",
-                          "webConsoleDefinition",
-                          "debuggerDefinition",
-                          "inspectorDefinition",
-                          "styleEditorDefinition",
-                          "netMonitorDefinition"
-                        ];
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-const inspectorProps = "chrome://browser/locale/devtools/inspector.properties";
-const debuggerProps = "chrome://browser/locale/devtools/debugger.properties";
-const styleEditorProps = "chrome://browser/locale/devtools/styleeditor.properties";
-const webConsoleProps = "chrome://browser/locale/devtools/webconsole.properties";
-const profilerProps = "chrome://browser/locale/devtools/profiler.properties";
-const netMonitorProps = "chrome://browser/locale/devtools/netmonitor.properties";
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "osString",
-  function() Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS);
-
-// Panels
-XPCOMUtils.defineLazyModuleGetter(this, "WebConsolePanel",
-  "resource:///modules/WebConsolePanel.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "DebuggerPanel",
-  "resource:///modules/devtools/DebuggerPanel.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "StyleEditorPanel",
-  "resource:///modules/devtools/StyleEditorPanel.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "InspectorPanel",
-  "resource:///modules/devtools/InspectorPanel.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "ProfilerPanel",
-  "resource:///modules/devtools/ProfilerPanel.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "NetMonitorPanel",
-  "resource:///modules/devtools/NetMonitorPanel.jsm");
-
-// Strings
-XPCOMUtils.defineLazyGetter(this, "webConsoleStrings",
-  function() Services.strings.createBundle(webConsoleProps));
-
-XPCOMUtils.defineLazyGetter(this, "debuggerStrings",
-  function() Services.strings.createBundle(debuggerProps));
-
-XPCOMUtils.defineLazyGetter(this, "styleEditorStrings",
-  function() Services.strings.createBundle(styleEditorProps));
-
-XPCOMUtils.defineLazyGetter(this, "inspectorStrings",
-  function() Services.strings.createBundle(inspectorProps));
-
-XPCOMUtils.defineLazyGetter(this, "profilerStrings",
-  function() Services.strings.createBundle(profilerProps));
-
-XPCOMUtils.defineLazyGetter(this, "netMonitorStrings",
-  function() Services.strings.createBundle(netMonitorProps));
-
-// Definitions
-let webConsoleDefinition = {
-  id: "webconsole",
-  key: l10n("cmd.commandkey", webConsoleStrings),
-  accesskey: l10n("webConsoleCmd.accesskey", webConsoleStrings),
-  modifiers: Services.appinfo.OS == "Darwin" ? "accel,alt" : "accel,shift",
-  ordinal: 0,
-  icon: "chrome://browser/skin/devtools/tool-webconsole.png",
-  url: "chrome://browser/content/devtools/webconsole.xul",
-  label: l10n("ToolboxWebconsole.label", webConsoleStrings),
-  tooltip: l10n("ToolboxWebconsole.tooltip", webConsoleStrings),
-
-  isTargetSupported: function(target) {
-    return true;
-  },
-  build: function(iframeWindow, toolbox) {
-    let panel = new WebConsolePanel(iframeWindow, toolbox);
-    return panel.open();
-  }
-};
-
-let debuggerDefinition = {
-  id: "jsdebugger",
-  key: l10n("open.commandkey", debuggerStrings),
-  accesskey: l10n("debuggerMenu.accesskey", debuggerStrings),
-  modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
-  ordinal: 2,
-  killswitch: "devtools.debugger.enabled",
-  icon: "chrome://browser/skin/devtools/tool-debugger.png",
-  url: "chrome://browser/content/debugger.xul",
-  label: l10n("ToolboxDebugger.label", debuggerStrings),
-  tooltip: l10n("ToolboxDebugger.tooltip", debuggerStrings),
-
-  isTargetSupported: function(target) {
-    return true;
-  },
-
-  build: function(iframeWindow, toolbox) {
-    let panel = new DebuggerPanel(iframeWindow, toolbox);
-    return panel.open();
-  }
-};
-
-let inspectorDefinition = {
-  id: "inspector",
-  accesskey: l10n("inspector.accesskey", inspectorStrings),
-  key: l10n("inspector.commandkey", inspectorStrings),
-  ordinal: 1,
-  modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
-  icon: "chrome://browser/skin/devtools/tool-inspector.png",
-  url: "chrome://browser/content/devtools/inspector/inspector.xul",
-  label: l10n("inspector.label", inspectorStrings),
-  tooltip: l10n("inspector.tooltip", inspectorStrings),
-
-  isTargetSupported: function(target) {
-    return !target.isRemote;
-  },
-
-  build: function(iframeWindow, toolbox) {
-    let panel = new InspectorPanel(iframeWindow, toolbox);
-    return panel.open();
-  }
-};
-
-let styleEditorDefinition = {
-  id: "styleeditor",
-  key: l10n("open.commandkey", styleEditorStrings),
-  ordinal: 3,
-  accesskey: l10n("open.accesskey", styleEditorStrings),
-  modifiers: "shift",
-  icon: "chrome://browser/skin/devtools/tool-styleeditor.png",
-  url: "chrome://browser/content/styleeditor.xul",
-  label: l10n("ToolboxStyleEditor.label", styleEditorStrings),
-  tooltip: l10n("ToolboxStyleEditor.tooltip", styleEditorStrings),
-
-  isTargetSupported: function(target) {
-    return true;
-  },
-
-  build: function(iframeWindow, toolbox) {
-    let panel = new StyleEditorPanel(iframeWindow, toolbox);
-    return panel.open();
-  }
-};
-
-let profilerDefinition = {
-  id: "jsprofiler",
-  accesskey: l10n("profiler.accesskey", profilerStrings),
-  key: l10n("profiler2.commandkey", profilerStrings),
-  ordinal: 4,
-  modifiers: "shift",
-  killswitch: "devtools.profiler.enabled",
-  icon: "chrome://browser/skin/devtools/tool-profiler.png",
-  url: "chrome://browser/content/profiler.xul",
-  label: l10n("profiler.label", profilerStrings),
-  tooltip: l10n("profiler.tooltip", profilerStrings),
-
-  isTargetSupported: function (target) {
-    return true;
-  },
-
-  build: function (frame, target) {
-    let panel = new ProfilerPanel(frame, target);
-    return panel.open();
-  }
-};
-
-let netMonitorDefinition = {
-  id: "netmonitor",
-  accesskey: l10n("netmonitor.accesskey", netMonitorStrings),
-  key: l10n("netmonitor.commandkey", netMonitorStrings),
-  ordinal: 5,
-  modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
-  killswitch: "devtools.netmonitor.enabled",
-  icon: "chrome://browser/skin/devtools/tool-profiler.png",
-  url: "chrome://browser/content/devtools/netmonitor.xul",
-  label: l10n("netmonitor.label", netMonitorStrings),
-  tooltip: l10n("netmonitor.tooltip", netMonitorStrings),
-
-  isTargetSupported: function(target) {
-    return true;
-  },
-
-  build: function(iframeWindow, toolbox) {
-    let panel = new NetMonitorPanel(iframeWindow, toolbox);
-    return panel.open();
-  }
-};
-
-this.defaultTools = [
-  styleEditorDefinition,
-  webConsoleDefinition,
-  debuggerDefinition,
-  inspectorDefinition,
-  profilerDefinition,
-  netMonitorDefinition
-];
-
-/**
- * Lookup l10n string from a string bundle.
- *
- * @param {string} name
- *        The key to lookup.
- * @param {StringBundle} bundle
- *        The key to lookup.
- * @returns A localized version of the given key.
- */
-function l10n(name, bundle)
-{
-  try {
-    return bundle.GetStringFromName(name);
-  } catch (ex) {
-    Services.console.logStringMessage("Error reading '" + name + "'");
-    throw new Error("l10n error with " + name);
-  }
-}
deleted file mode 100644
--- a/browser/devtools/framework/Toolbox.jsm
+++ /dev/null
@@ -1,784 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-Cu.import("resource:///modules/devtools/gDevTools.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Hosts",
-                                  "resource:///modules/devtools/ToolboxHosts.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "CommandUtils",
-                                  "resource:///modules/devtools/DeveloperToolbar.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "toolboxStrings", function() {
-  let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
-  let l10n = function(aName, ...aArgs) {
-    try {
-      if (aArgs.length == 0) {
-        return bundle.GetStringFromName(aName);
-      } else {
-        return bundle.formatStringFromName(aName, aArgs, aArgs.length);
-      }
-    } catch (ex) {
-      Services.console.logStringMessage("Error reading '" + aName + "'");
-    }
-  };
-  return l10n;
-});
-
-XPCOMUtils.defineLazyGetter(this, "Requisition", function() {
-  Cu.import("resource://gre/modules/devtools/Require.jsm");
-  Cu.import("resource:///modules/devtools/gcli.jsm");
-
-  return require('gcli/cli').Requisition;
-});
-
-this.EXPORTED_SYMBOLS = [ "Toolbox" ];
-
-// This isn't the best place for this, but I don't know what is right now
-
-/**
- * Implementation of 'promised', while we wait for bug 790195 to be fixed.
- * @see Consuming promises in https://addons.mozilla.org/en-US/developers/docs/sdk/latest/packages/api-utils/promise.html
- * @see https://bugzilla.mozilla.org/show_bug.cgi?id=790195
- * @see https://github.com/mozilla/addon-sdk/blob/master/packages/api-utils/lib/promise.js#L179
- */
-Promise.promised = (function() {
-  // Note: Define shortcuts and utility functions here in order to avoid
-  // slower property accesses and unnecessary closure creations on each
-  // call of this popular function.
-
-  var call = Function.call;
-  var concat = Array.prototype.concat;
-
-  // Utility function that does following:
-  // execute([ f, self, args...]) => f.apply(self, args)
-  function execute(args) { return call.apply(call, args); }
-
-  // Utility function that takes promise of `a` array and maybe promise `b`
-  // as arguments and returns promise for `a.concat(b)`.
-  function promisedConcat(promises, unknown) {
-    return promises.then(function(values) {
-      return Promise.resolve(unknown).then(function(value) {
-        return values.concat([ value ]);
-      });
-    });
-  }
-
-  return function promised(f, prototype) {
-    /**
-    Returns a wrapped `f`, which when called returns a promise that resolves to
-    `f(...)` passing all the given arguments to it, which by the way may be
-    promises. Optionally second `prototype` argument may be provided to be used
-    a prototype for a returned promise.
-
-    ## Example
-
-    var promise = promised(Array)(1, promise(2), promise(3))
-    promise.then(console.log) // => [ 1, 2, 3 ]
-    **/
-
-    return function promised() {
-      // create array of [ f, this, args... ]
-      return concat.apply([ f, this ], arguments).
-          // reduce it via `promisedConcat` to get promised array of fulfillments
-          reduce(promisedConcat, Promise.resolve([], prototype)).
-          // finally map that to promise of `f.apply(this, args...)`
-          then(execute);
-    };
-  };
-})();
-
-/**
- * Convert an array of promises to a single promise, which is resolved (with an
- * array containing resolved values) only when all the component promises are
- * resolved.
- */
-Promise.all = Promise.promised(Array);
-
-
-
-
-/**
- * A "Toolbox" is the component that holds all the tools for one specific
- * target. Visually, it's a document that includes the tools tabs and all
- * the iframes where the tool panels will be living in.
- *
- * @param {object} target
- *        The object the toolbox is debugging.
- * @param {string} selectedTool
- *        Tool to select initially
- * @param {Toolbox.HostType} hostType
- *        Type of host that will host the toolbox (e.g. sidebar, window)
- */
-this.Toolbox = function Toolbox(target, selectedTool, hostType) {
-  this._target = target;
-  this._toolPanels = new Map();
-
-  this._toolRegistered = this._toolRegistered.bind(this);
-  this._toolUnregistered = this._toolUnregistered.bind(this);
-  this.destroy = this.destroy.bind(this);
-
-  this._target.on("close", this.destroy);
-
-  if (!hostType) {
-    hostType = Services.prefs.getCharPref(this._prefs.LAST_HOST);
-  }
-  if (!selectedTool) {
-    selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
-  }
-  let definitions = gDevTools.getToolDefinitionMap();
-  if (!definitions.get(selectedTool) && selectedTool != "options") {
-    selectedTool = "webconsole";
-  }
-  this._defaultToolId = selectedTool;
-
-  this._host = this._createHost(hostType);
-
-  EventEmitter.decorate(this);
-
-  this._refreshHostTitle = this._refreshHostTitle.bind(this);
-  this._target.on("navigate", this._refreshHostTitle);
-  this.on("host-changed", this._refreshHostTitle);
-  this.on("select", this._refreshHostTitle);
-
-  gDevTools.on("tool-registered", this._toolRegistered);
-  gDevTools.on("tool-unregistered", this._toolUnregistered);
-}
-
-/**
- * The toolbox can be 'hosted' either embedded in a browser window
- * or in a separate window.
- */
-Toolbox.HostType = {
-  BOTTOM: "bottom",
-  SIDE: "side",
-  WINDOW: "window"
-}
-
-Toolbox.prototype = {
-  _URL: "chrome://browser/content/devtools/framework/toolbox.xul",
-
-  _prefs: {
-    LAST_HOST: "devtools.toolbox.host",
-    LAST_TOOL: "devtools.toolbox.selectedTool",
-    SIDE_ENABLED: "devtools.toolbox.sideEnabled"
-  },
-
-  HostType: Toolbox.HostType,
-
-  /**
-   * Returns a *copy* of the _toolPanels collection.
-   *
-   * @return {Map} panels
-   *         All the running panels in the toolbox
-   */
-  getToolPanels: function TB_getToolPanels() {
-    let panels = new Map();
-
-    for (let [key, value] of this._toolPanels) {
-      panels.set(key, value);
-    }
-    return panels;
-  },
-
-  /**
-   * Access the panel for a given tool
-   */
-  getPanel: function TBOX_getPanel(id) {
-    return this.getToolPanels().get(id);
-  },
-
-  /**
-   * This is a shortcut for getPanel(currentToolId) because it is much more
-   * likely that we're going to want to get the panel that we've just made
-   * visible
-   */
-  getCurrentPanel: function TBOX_getCurrentPanel() {
-    return this.getToolPanels().get(this.currentToolId);
-  },
-
-  /**
-   * Get/alter the target of a Toolbox so we're debugging something different.
-   * See Target.jsm for more details.
-   * TODO: Do we allow |toolbox.target = null;| ?
-   */
-  get target() {
-    return this._target;
-  },
-
-  /**
-   * Get/alter the host of a Toolbox, i.e. is it in browser or in a separate
-   * tab. See HostType for more details.
-   */
-  get hostType() {
-    return this._host.type;
-  },
-
-  /**
-   * Get/alter the currently displayed tool.
-   */
-  get currentToolId() {
-    return this._currentToolId;
-  },
-
-  set currentToolId(value) {
-    this._currentToolId = value;
-  },
-
-  /**
-   * Get the iframe containing the toolbox UI.
-   */
-  get frame() {
-    return this._host.frame;
-  },
-
-  /**
-   * Shortcut to the document containing the toolbox UI
-   */
-  get doc() {
-    return this.frame.contentDocument;
-  },
-
-  /**
-   * Open the toolbox
-   */
-  open: function TBOX_open() {
-    let deferred = Promise.defer();
-
-    this._host.create().then(function(iframe) {
-      let domReady = function() {
-        iframe.removeEventListener("DOMContentLoaded", domReady, true);
-
-        this.isReady = true;
-
-        let closeButton = this.doc.getElementById("toolbox-close");
-        closeButton.addEventListener("command", this.destroy, true);
-
-        this._buildDockButtons();
-        this._buildOptions();
-        this._buildTabs();
-        this._buildButtons();
-        this._addKeysToWindow();
-
-        this.selectTool(this._defaultToolId).then(function(panel) {
-          this.emit("ready");
-          deferred.resolve();
-        }.bind(this));
-      }.bind(this);
-
-      iframe.addEventListener("DOMContentLoaded", domReady, true);
-      iframe.setAttribute("src", this._URL);
-    }.bind(this));
-
-    return deferred.promise;
-  },
-
-  _buildOptions: function TBOX__buildOptions() {
-    this.optionsButton = this.doc.getElementById("toolbox-tab-options");
-    this.optionsButton.addEventListener("command", function() {
-      this.selectTool("options");
-    }.bind(this), false);
-
-    let iframe = this.doc.getElementById("toolbox-panel-iframe-options");
-    this._toolPanels.set("options", iframe);
-
-    let key = this.doc.getElementById("toolbox-options-key");
-    key.addEventListener("command", function(toolId) {
-      this.selectTool(toolId);
-    }.bind(this, "options"), true);
-  },
-
-  /**
-   * Adds the keys and commands to the Toolbox Window in window mode.
-   */
-  _addKeysToWindow: function TBOX__addKeysToWindow() {
-    if (this.hostType != Toolbox.HostType.WINDOW) {
-      return;
-    }
-    let doc = this.doc.defaultView.parent.document;
-    for (let [id, toolDefinition] of gDevTools.getToolDefinitionMap()) {
-      if (toolDefinition.key) {
-        // Prevent multiple entries for the same tool.
-        if (doc.getElementById("key_" + id)) {
-          continue;
-        }
-        let key = doc.createElement("key");
-        key.id = "key_" + id;
-
-        if (toolDefinition.key.startsWith("VK_")) {
-          key.setAttribute("keycode", toolDefinition.key);
-        } else {
-          key.setAttribute("key", toolDefinition.key);
-        }
-
-        key.setAttribute("modifiers", toolDefinition.modifiers);
-        key.setAttribute("oncommand", "void(0);"); // needed. See bug 371900
-        key.addEventListener("command", function(toolId) {
-          this.selectTool(toolId);
-        }.bind(this, id), true);
-        doc.getElementById("toolbox-keyset").appendChild(key);
-      }
-    }
-  },
-
-  /**
-   * Build the buttons for changing hosts. Called every time
-   * the host changes.
-   */
-  _buildDockButtons: function TBOX_createDockButtons() {
-    let dockBox = this.doc.getElementById("toolbox-dock-buttons");
-
-    while (dockBox.firstChild) {
-      dockBox.removeChild(dockBox.firstChild);
-    }
-
-    if (!this._target.isLocalTab) {
-      return;
-    }
-
-    let closeButton = this.doc.getElementById("toolbox-close");
-    if (this.hostType === this.HostType.WINDOW) {
-      closeButton.setAttribute("hidden", "true");
-    } else {
-      closeButton.removeAttribute("hidden");
-    }
-
-    let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);
-
-    for each (let position in this.HostType) {
-      if (position == this.hostType ||
-         (!sideEnabled && position == this.HostType.SIDE)) {
-        continue;
-      }
-
-      let button = this.doc.createElement("toolbarbutton");
-      button.id = "toolbox-dock-" + position;
-      button.className = "toolbox-dock-button";
-      button.setAttribute("tooltiptext", toolboxStrings("toolboxDockButtons." +
-                                                        position + ".tooltip"));
-      button.addEventListener("command", function(position) {
-        this.switchHost(position);
-      }.bind(this, position));
-
-      dockBox.appendChild(button);
-    }
-  },
-
-  /**
-   * Add tabs to the toolbox UI for registered tools
-   */
-  _buildTabs: function TBOX_buildTabs() {
-    for (let definition of gDevTools.getToolDefinitionArray()) {
-      this._buildTabForTool(definition);
-    }
-  },
-
-  /**
-   * Add buttons to the UI as specified in the devtools.window.toolbarSpec pref
-   */
-  _buildButtons: function TBOX_buildButtons() {
-    if (!this.target.isLocalTab) {
-      return;
-    }
-
-    let toolbarSpec = CommandUtils.getCommandbarSpec("devtools.toolbox.toolbarSpec");
-    let environment = { chromeDocument: this.target.tab.ownerDocument };
-    let requisition = new Requisition(environment);
-
-    let buttons = CommandUtils.createButtons(toolbarSpec, this._target, this.doc, requisition);
-
-    let container = this.doc.getElementById("toolbox-buttons");
-    buttons.forEach(function(button) {
-      container.appendChild(button);
-    }.bind(this));
-  },
-
-  /**
-   * Build a tab for one tool definition and add to the toolbox
-   *
-   * @param {string} toolDefinition
-   *        Tool definition of the tool to build a tab for.
-   */
-  _buildTabForTool: function TBOX_buildTabForTool(toolDefinition) {
-    if (!toolDefinition.isTargetSupported(this._target)) {
-      return;
-    }
-
-    let tabs = this.doc.getElementById("toolbox-tabs");
-    let deck = this.doc.getElementById("toolbox-deck");
-
-    let id = toolDefinition.id;
-
-    let radio = this.doc.createElement("radio");
-    radio.className = "toolbox-tab devtools-tab";
-    radio.id = "toolbox-tab-" + id;
-    radio.setAttribute("flex", "1");
-    radio.setAttribute("toolid", id);
-    radio.setAttribute("tooltiptext", toolDefinition.tooltip);
-
-    radio.addEventListener("command", function(id) {
-      this.selectTool(id);
-    }.bind(this, id));
-
-    if (toolDefinition.icon) {
-      let image = this.doc.createElement("image");
-      image.setAttribute("src", toolDefinition.icon);
-      radio.appendChild(image);
-    }
-
-    let label = this.doc.createElement("label");
-    label.setAttribute("value", toolDefinition.label)
-    label.setAttribute("crop", "end");
-    label.setAttribute("flex", "1");
-
-    let vbox = this.doc.createElement("vbox");
-    vbox.className = "toolbox-panel";
-    vbox.id = "toolbox-panel-" + id;
-
-    radio.appendChild(label);
-    tabs.appendChild(radio);
-    deck.appendChild(vbox);
-
-    this._addKeysToWindow();
-  },
-
-  /**
-   * Switch to the tool with the given id
-   *
-   * @param {string} id
-   *        The id of the tool to switch to
-   */
-  selectTool: function TBOX_selectTool(id) {
-    let deferred = Promise.defer();
-
-    let selected = this.doc.querySelector(".devtools-tab[selected]");
-    if (selected) {
-      selected.removeAttribute("selected");
-    }
-    let tab = this.doc.getElementById("toolbox-tab-" + id);
-    tab.setAttribute("selected", "true");
-
-    if (this._currentToolId == id) {
-      // Return the existing panel in order to have a consistent return value.
-      return Promise.resolve(this._toolPanels.get(id));
-    }
-
-    if (!this.isReady) {
-      throw new Error("Can't select tool, wait for toolbox 'ready' event");
-    }
-    let tab = this.doc.getElementById("toolbox-tab-" + id);
-
-    if (!tab) {
-      throw new Error("No tool found");
-    }
-
-    let tabstrip = this.doc.getElementById("toolbox-tabs");
-
-    // select the right tab
-    let index = -1;
-    let tabs = tabstrip.childNodes;
-    for (let i = 0; i < tabs.length; i++) {
-      if (tabs[i] === tab) {
-        index = i;
-        break;
-      }
-    }
-    tabstrip.selectedItem = tab;
-
-    // and select the right iframe
-    let deck = this.doc.getElementById("toolbox-deck");
-    // offset by 1 due to options panel
-    if (id == "options") {
-      deck.selectedIndex = 0;
-      this.optionsButton.setAttribute("checked", true);
-    }
-    else {
-      deck.selectedIndex = index != -1 ? index + 1: -1;
-      this.optionsButton.removeAttribute("checked");
-    }
-
-    let definition = gDevTools.getToolDefinitionMap().get(id);
-
-    this._currentToolId = id;
-
-    let resolveSelected = panel => {
-      this.emit("select", id);
-      this.emit(id + "-selected", panel);
-      deferred.resolve(panel);
-    };
-
-    let iframe = this.doc.getElementById("toolbox-panel-iframe-" + id);
-    if (!iframe) {
-      iframe = this.doc.createElement("iframe");
-      iframe.className = "toolbox-panel-iframe";
-      iframe.id = "toolbox-panel-iframe-" + id;
-      iframe.setAttribute("flex", 1);
-      iframe.setAttribute("forceOwnRefreshDriver", "");
-      iframe.tooltip = "aHTMLTooltip";
-
-      let vbox = this.doc.getElementById("toolbox-panel-" + id);
-      vbox.appendChild(iframe);
-
-      let boundLoad = function() {
-        iframe.removeEventListener("DOMContentLoaded", boundLoad, true);
-
-        let built = definition.build(iframe.contentWindow, this);
-        Promise.resolve(built).then(function(panel) {
-          this._toolPanels.set(id, panel);
-
-          this.emit(id + "-ready", panel);
-          gDevTools.emit(id + "-ready", this, panel);
-
-          resolveSelected(panel);
-        }.bind(this));
-      }.bind(this);
-
-      iframe.addEventListener("DOMContentLoaded", boundLoad, true);
-      iframe.setAttribute("src", definition.url);
-    } else {
-      let panel = this._toolPanels.get(id);
-      // only emit 'select' event if the iframe has been loaded
-      if (panel && (!panel.contentDocument ||
-                    panel.contentDocument.readyState == "complete")) {
-        resolveSelected(panel);
-      }
-      else if (panel) {
-        let boundLoad = function() {
-          panel.removeEventListener("DOMContentLoaded", boundLoad, true);
-          resolveSelected(panel);
-        };
-        panel.addEventListener("DOMContentLoaded", boundLoad, true);
-      }
-    }
-
-    if (id != "options") {
-      Services.prefs.setCharPref(this._prefs.LAST_TOOL, id);
-    }
-
-    return deferred.promise;
-  },
-
-  /**
-   * Raise the toolbox host.
-   */
-  raise: function TBOX_raise() {
-    this._host.raise();
-  },
-
-  /**
-   * Refresh the host's title.
-   */
-  _refreshHostTitle: function TBOX_refreshHostTitle() {
-    let toolName;
-    let toolId = this.currentToolId;
-    let toolDef = gDevTools.getToolDefinitionMap().get(toolId);
-    if (toolDef) {
-      toolName = toolDef.label;
-    } else {
-      // no tool is selected
-      toolName = toolboxStrings("toolbox.defaultTitle");
-    }
-    let title = toolboxStrings("toolbox.titleTemplate",
-                               toolName, this.target.url);
-    this._host.setTitle(title);
-  },
-
-  /**
-   * Create a host object based on the given host type.
-   *
-   * Warning: some hosts require that the toolbox target provides a reference to
-   * the attached tab. Not all Targets have a tab property - make sure you correctly
-   * mix and match hosts and targets.
-   *
-   * @param {string} hostType
-   *        The host type of the new host object
-   *
-   * @return {Host} host
-   *        The created host object
-   */
-  _createHost: function TBOX_createHost(hostType) {
-    if (!Hosts[hostType]) {
-      throw new Error('Unknown hostType: '+ hostType);
-    }
-    let newHost = new Hosts[hostType](this.target.tab);
-
-    // clean up the toolbox if its window is closed
-    newHost.on("window-closed", this.destroy);
-
-    return newHost;
-  },
-
-  /**
-   * Switch to a new host for the toolbox UI. E.g.
-   * bottom, sidebar, separate window.
-   *
-   * @param {string} hostType
-   *        The host type of the new host object
-   */
-  switchHost: function TBOX_switchHost(hostType) {
-    if (hostType == this._host.type) {
-      return;
-    }
-
-    if (!this._target.isLocalTab) {
-      return;
-    }
-
-    let newHost = this._createHost(hostType);
-    return newHost.create().then(function(iframe) {
-      // change toolbox document's parent to the new host
-      iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
-      iframe.swapFrameLoaders(this.frame);
-
-      this._host.off("window-closed", this.destroy);
-      this._host.destroy();
-
-      this._host = newHost;
-
-      Services.prefs.setCharPref(this._prefs.LAST_HOST, this._host.type);
-
-      this._buildDockButtons();
-      this._addKeysToWindow();
-
-      this.emit("host-changed");
-    }.bind(this));
-  },
-
-  /**
-   * Handler for the tool-registered event.
-   * @param  {string} event
-   *         Name of the event ("tool-registered")
-   * @param  {string} toolId
-   *         Id of the tool that was registered
-   */
-  _toolRegistered: function TBOX_toolRegistered(event, toolId) {
-    let defs = gDevTools.getToolDefinitionMap();
-    let tool = defs.get(toolId);
-
-    this._buildTabForTool(tool);
-  },
-
-  /**
-   * Handler for the tool-unregistered event.
-   * @param  {string} event
-   *         Name of the event ("tool-unregistered")
-   * @param  {string|object} toolId
-   *         Definition or id of the tool that was unregistered. Passing the
-   *         tool id should be avoided as it is a temporary measure.
-   */
-  _toolUnregistered: function TBOX_toolUnregistered(event, toolId) {
-    if (typeof toolId != "string") {
-      toolId = toolId.id;
-    }
-
-    if (this._toolPanels.has(toolId)) {
-      let instance = this._toolPanels.get(toolId);
-      instance.destroy();
-      this._toolPanels.delete(toolId);
-    }
-
-    let radio = this.doc.getElementById("toolbox-tab-" + toolId);
-    let panel = this.doc.getElementById("toolbox-panel-" + toolId);
-
-    if (radio) {
-      if (this._currentToolId == toolId) {
-        let nextToolName = null;
-        if (radio.nextSibling) {
-          nextToolName = radio.nextSibling.getAttribute("toolid");
-        }
-        if (radio.previousSibling) {
-          nextToolName = radio.previousSibling.getAttribute("toolid");
-        }
-        if (nextToolName) {
-          this.selectTool(nextToolName);
-        }
-      }
-      radio.parentNode.removeChild(radio);
-    }
-
-    if (panel) {
-      panel.parentNode.removeChild(panel);
-    }
-
-    if (this.hostType == Toolbox.HostType.WINDOW) {
-      let doc = this.doc.defaultView.parent.document;
-      let key = doc.getElementById("key_" + id);
-      if (key) {
-        key.parentNode.removeChild(key);
-      }
-    }
-  },
-
-
-  /**
-   * Get the toolbox's notification box
-   *
-   * @return The notification box element.
-   */
-  getNotificationBox: function TBOX_getNotificationBox() {
-    return this.doc.getElementById("toolbox-notificationbox");
-  },
-
-  /**
-   * Remove all UI elements, detach from target and clear up
-   */
-  destroy: function TBOX_destroy() {
-    // If several things call destroy then we give them all the same
-    // destruction promise so we're sure to destroy only once
-    if (this._destroyer) {
-      return this._destroyer;
-    }
-    // Assign the "_destroyer" property before calling the other
-    // destroyer methods to guarantee that the Toolbox's destroy
-    // method is only executed once.
-    let deferred = Promise.defer();
-    this._destroyer = deferred.promise;
-
-    this._target.off("navigate", this._refreshHostTitle);
-    this.off("select", this._refreshHostTitle);
-    this.off("host-changed", this._refreshHostTitle);
-
-    gDevTools.off("tool-registered", this._toolRegistered);
-    gDevTools.off("tool-unregistered", this._toolUnregistered);
-
-    let outstanding = [];
-
-    this._toolPanels.delete("options");
-    for (let [id, panel] of this._toolPanels) {
-      outstanding.push(panel.destroy());
-    }
-
-    let container = this.doc.getElementById("toolbox-buttons");
-    while(container.firstChild) {
-      container.removeChild(container.firstChild);
-    }
-
-    outstanding.push(this._host.destroy());
-
-    // Targets need to be notified that the toolbox is being torn down, so that
-    // remote protocol connections can be gracefully terminated.
-    if (this._target) {
-      this._target.off("close", this.destroy);
-      outstanding.push(this._target.destroy());
-    }
-    this._target = null;
-
-    Promise.all(outstanding).then(function() {
-      this.emit("destroyed");
-      // Free _host after the call to destroyed in order to let a chance
-      // to destroyed listeners to still query toolbox attributes
-      this._host = null;
-      deferred.resolve();
-    }.bind(this));
-
-    return this._destroyer;
-  }
-};
deleted file mode 100644
--- a/browser/devtools/framework/ToolboxHosts.jsm
+++ /dev/null
@@ -1,283 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-
-this.EXPORTED_SYMBOLS = [ "Hosts" ];
-
-/**
- * A toolbox host represents an object that contains a toolbox (e.g. the
- * sidebar or a separate window). Any host object should implement the
- * following functions:
- *
- * create() - create the UI and emit a 'ready' event when the UI is ready to use
- * destroy() - destroy the host's UI
- */
-
-this.Hosts = {
-  "bottom": BottomHost,
-  "side": SidebarHost,
-  "window": WindowHost
-}
-
-/**
- * Host object for the dock on the bottom of the browser
- */
-function BottomHost(hostTab) {
-  this.hostTab = hostTab;
-
-  EventEmitter.decorate(this);
-}
-
-BottomHost.prototype = {
-  type: "bottom",
-
-  heightPref: "devtools.toolbox.footer.height",
-
-  /**
-   * Create a box at the bottom of the host tab.
-   */
-  create: function BH_create() {
-    let deferred = Promise.defer();
-
-    let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
-    let ownerDocument = gBrowser.ownerDocument;
-
-    this._splitter = ownerDocument.createElement("splitter");
-    this._splitter.setAttribute("class", "devtools-horizontal-splitter");
-
-    this.frame = ownerDocument.createElement("iframe");
-    this.frame.className = "devtools-toolbox-bottom-iframe";
-    this.frame.height = Services.prefs.getIntPref(this.heightPref);
-
-    this._nbox = gBrowser.getNotificationBox(this.hostTab.linkedBrowser);
-    this._nbox.appendChild(this._splitter);
-    this._nbox.appendChild(this.frame);
-
-    let frameLoad = function() {
-      this.frame.removeEventListener("DOMContentLoaded", frameLoad, true);
-      this.emit("ready", this.frame);
-
-      deferred.resolve(this.frame);
-    }.bind(this);
-
-    this.frame.tooltip = "aHTMLTooltip";
-    this.frame.addEventListener("DOMContentLoaded", frameLoad, true);
-
-    // we have to load something so we can switch documents if we have to
-    this.frame.setAttribute("src", "about:blank");
-
-    focusTab(this.hostTab);
-
-    return deferred.promise;
-  },
-
-  /**
-   * Raise the host.
-   */
-  raise: function BH_raise() {
-    focusTab(this.hostTab);
-  },
-
-  /**
-   * Set the toolbox title.
-   */
-  setTitle: function BH_setTitle(title) {
-    // Nothing to do for this host type.
-  },
-
-  /**
-   * Destroy the bottom dock.
-   */
-  destroy: function BH_destroy() {
-    if (!this._destroyed) {
-      this._destroyed = true;
-
-      Services.prefs.setIntPref(this.heightPref, this.frame.height);
-      this._nbox.removeChild(this._splitter);
-      this._nbox.removeChild(this.frame);
-    }
-
-    return Promise.resolve(null);
-  }
-}
-
-
-/**
- * Host object for the in-browser sidebar
- */
-function SidebarHost(hostTab) {
-  this.hostTab = hostTab;
-
-  EventEmitter.decorate(this);
-}
-
-SidebarHost.prototype = {
-  type: "side",
-
-  widthPref: "devtools.toolbox.sidebar.width",
-
-  /**
-   * Create a box in the sidebar of the host tab.
-   */
-  create: function SH_create() {
-    let deferred = Promise.defer();
-
-    let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
-    let ownerDocument = gBrowser.ownerDocument;
-
-    this._splitter = ownerDocument.createElement("splitter");
-    this._splitter.setAttribute("class", "devtools-side-splitter");
-
-    this.frame = ownerDocument.createElement("iframe");
-    this.frame.className = "devtools-toolbox-side-iframe";
-    this.frame.width = Services.prefs.getIntPref(this.widthPref);
-
-    this._sidebar = gBrowser.getSidebarContainer(this.hostTab.linkedBrowser);
-    this._sidebar.appendChild(this._splitter);
-    this._sidebar.appendChild(this.frame);
-
-    let frameLoad = function() {
-      this.frame.removeEventListener("DOMContentLoaded", frameLoad, true);
-      this.emit("ready", this.frame);
-
-      deferred.resolve(this.frame);
-    }.bind(this);
-
-    this.frame.addEventListener("DOMContentLoaded", frameLoad, true);
-    this.frame.tooltip = "aHTMLTooltip";
-    this.frame.setAttribute("src", "about:blank");
-
-    focusTab(this.hostTab);
-
-    return deferred.promise;
-  },
-
-  /**
-   * Raise the host.
-   */
-  raise: function SH_raise() {
-    focusTab(this.hostTab);
-  },
-
-  /**
-   * Set the toolbox title.
-   */
-  setTitle: function SH_setTitle(title) {
-    // Nothing to do for this host type.
-  },
-
-  /**
-   * Destroy the sidebar.
-   */
-  destroy: function SH_destroy() {
-    if (!this._destroyed) {
-      this._destroyed = true;
-
-      Services.prefs.setIntPref(this.widthPref, this.frame.width);
-      this._sidebar.removeChild(this._splitter);
-      this._sidebar.removeChild(this.frame);
-    }
-
-    return Promise.resolve(null);
-  }
-}
-
-/**
- * Host object for the toolbox in a separate window
- */
-function WindowHost() {
-  this._boundUnload = this._boundUnload.bind(this);
-
-  EventEmitter.decorate(this);
-}
-
-WindowHost.prototype = {
-  type: "window",
-
-  WINDOW_URL: "chrome://browser/content/devtools/framework/toolbox-window.xul",
-
-  /**
-   * Create a new xul window to contain the toolbox.
-   */
-  create: function WH_create() {
-    let deferred = Promise.defer();
-
-    let flags = "chrome,centerscreen,resizable,dialog=no";
-    let win = Services.ww.openWindow(null, this.WINDOW_URL, "_blank",
-                                     flags, null);
-
-    let frameLoad = function(event) {
-      win.removeEventListener("load", frameLoad, true);
-      this.frame = win.document.getElementById("toolbox-iframe");
-      this.emit("ready", this.frame);
-
-      deferred.resolve(this.frame);
-    }.bind(this);
-
-    win.addEventListener("load", frameLoad, true);
-    win.addEventListener("unload", this._boundUnload);
-
-    win.focus();
-
-    this._window = win;
-
-    return deferred.promise;
-  },
-
-  /**
-   * Catch the user closing the window.
-   */
-  _boundUnload: function(event) {
-    if (event.target.location != this.WINDOW_URL) {
-      return;
-    }
-    this._window.removeEventListener("unload", this._boundUnload);
-
-    this.emit("window-closed");
-  },
-
-  /**
-   * Raise the host.
-   */
-  raise: function RH_raise() {
-    this._window.focus();
-  },
-
-  /**
-   * Set the toolbox title.
-   */
-  setTitle: function WH_setTitle(title) {
-    this._window.document.title = title;
-  },
-
-  /**
-   * Destroy the window.
-   */
-  destroy: function WH_destroy() {
-    if (!this._destroyed) {
-      this._destroyed = true;
-
-      this._window.removeEventListener("unload", this._boundUnload);
-      this._window.close();
-    }
-
-    return Promise.resolve(null);
-  }
-}
-
-/**
- *  Switch to the given tab in a browser and focus the browser window
- */
-function focusTab(tab) {
-  let browserWindow = tab.ownerDocument.defaultView;
-  browserWindow.focus();
-  browserWindow.gBrowser.selectedTab = tab;
-}
--- a/browser/devtools/framework/connect/connect.js
+++ b/browser/devtools/framework/connect/connect.js
@@ -4,20 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cu = Components.utils;
 Cu.import("resource:///modules/devtools/Target.jsm");
 Cu.import("resource:///modules/devtools/Toolbox.jsm");
-Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
+let {devtools, gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 
 let gClient;
 let gConnectionTimeout;
 
 XPCOMUtils.defineLazyGetter(window, 'l10n', function () {
   return Services.strings.createBundle('chrome://browser/locale/devtools/connection-screen.properties');
 });
 
@@ -164,13 +164,13 @@ function handleConnectionTimeout() {
  * Opens the toolbox.
  */
 function openToolbox(form, chrome=false) {
   let options = {
     form: form,
     client: gClient,
     chrome: chrome
   };
-  TargetFactory.forRemoteTab(options).then((target) => {
-    gDevTools.showToolbox(target, "webconsole", Toolbox.HostType.WINDOW);
+  devtools.TargetFactory.forRemoteTab(options).then((target) => {
+    gDevTools.showToolbox(target, "webconsole", devtools.Toolbox.HostType.WINDOW);
     window.close();
   });
 }
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -1,27 +1,200 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-this.EXPORTED_SYMBOLS = [ "gDevTools", "DevTools", "gDevToolsBrowser" ];
+this.EXPORTED_SYMBOLS = [ "gDevTools", "DevTools", "gDevToolsBrowser", "devtools" ];
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource:///modules/devtools/shared/event-emitter.js");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-Cu.import("resource:///modules/devtools/ToolDefinitions.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Toolbox",
-  "resource:///modules/devtools/Toolbox.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
-  "resource:///modules/devtools/Target.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
+
+let loader = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}).Loader;
+
+// Used when the tools should be loaded from the Firefox package itself (the default)
+
+var BuiltinProvider = {
+  load: function(done) {
+    this.loader = new loader.Loader({
+      paths: {
+        "": "resource://gre/modules/commonjs/",
+        "main" : "resource:///modules/devtools/main",
+        "devtools": "resource:///modules/devtools",
+        "devtools/toolkit": "resource://gre/modules/devtools"
+      },
+      globals: {},
+    });
+    this.main = loader.main(this.loader, "main");
+
+    return Promise.resolve(undefined);
+  },
+
+  unload: function(reason) {
+    loader.unload(this.loader, reason);
+    delete this.loader;
+  },
+};
+
+var SrcdirProvider = {
+  load: function(done) {
+    let srcdir = Services.prefs.getComplexValue("devtools.loader.srcdir",
+                                                Ci.nsISupportsString);
+    srcdir = OS.Path.normalize(srcdir.data.trim());
+    let devtoolsDir = OS.Path.join(srcdir, "browser/devtools");
+    let toolkitDir = OS.Path.join(srcdir, "toolkit/devtools");
+
+    this.loader = new loader.Loader({
+      paths: {
+        "": "resource://gre/modules/commonjs/",
+        "devtools/toolkit": "file://" + toolkitDir,
+        "devtools": "file://" + devtoolsDir,
+        "main": "file://" + devtoolsDir + "/main.js"
+      },
+      globals: {}
+    });
+
+    this.main = loader.main(this.loader, "main");
+
+    return this._writeManifest(devtoolsDir).then((data) => {
+      this._writeManifest(toolkitDir);
+    }).then(null, Cu.reportError);
+  },
+
+  unload: function(reason) {
+    loader.unload(this.loader, reason);
+    delete this.loader;
+  },
+
+  _readFile: function(filename) {
+    let deferred = Promise.defer();
+    let file = new FileUtils.File(filename);
+    NetUtil.asyncFetch(file, (inputStream, status) => {
+      if (!Components.isSuccessCode(status)) {
+        deferred.reject(new Error("Couldn't load manifest: " + filename + "\n"));
+        return;
+      }
+      var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
+      deferred.resolve(data);
+    });
+    return deferred.promise;
+  },
+
+  _writeFile: function(filename, data) {
+    let deferred = Promise.defer();
+    let file = new FileUtils.File(filename);
+
+    var ostream = FileUtils.openSafeFileOutputStream(file)
+
+    var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+                    createInstance(Ci.nsIScriptableUnicodeConverter);
+    converter.charset = "UTF-8";
+    var istream = converter.convertToInputStream(data);
+    NetUtil.asyncCopy(istream, ostream, (status) => {
+      if (!Components.isSuccessCode(status)) {
+        deferred.reject(new Error("Couldn't write manifest: " + filename + "\n"));
+        return;
+      }
+
+      deferred.resolve(null);
+    });
+    return deferred.promise;
+  },
+
+  _writeManifest: function(dir) {
+    return this._readFile(dir + "/jar.mn").then((data) => {
+      // The file data is contained within inputStream.
+      // You can read it into a string with
+      let entries = [];
+      let lines = data.split(/\n/);
+      let preprocessed = /^\s*\*/;
+      let contentEntry = new RegExp("^\\s+content/(\\w+)/(\\S+)\\s+\\((\\S+)\\)");
+      for (let line of lines) {
+        if (preprocessed.test(line)) {
+          dump("Unable to override preprocessed file: " + line + "\n");
+          continue;
+        }
+        let match = contentEntry.exec(line);
+        if (match) {
+          let entry = "override chrome://" + match[1] + "/content/" + match[2] + "\tfile://" + dir + "/" + match[3];
+          entries.push(entry);
+        }
+      }
+      return this._writeFile(dir + "/chrome.manifest", entries.join("\n"));
+    }).then(() => {
+      Components.manager.addBootstrappedManifestLocation(new FileUtils.File(dir));
+    });
+  }
+};
+
+this.devtools = {
+  _provider: null,
+
+  get main() this._provider.main,
+
+  // This is a gross gross hack.  In one place (computed-view.js) we use
+  // Iterator, but the addon-sdk loader takes Iterator off the global.
+  // Give computed-view.js a way to get back to the Iterator until we have
+  // a chance to fix that crap.
+  _Iterator: Iterator,
+
+  setProvider: function(provider) {
+    if (provider === this._provider) {
+      return;
+    }
+
+    if (this._provider) {
+      delete this.require;
+      this._provider.unload("newprovider");
+      gDevTools._teardown();
+    }
+    this._provider = provider;
+    this._provider.load();
+    this.require = loader.Require(this._provider.loader, { id: "devtools" })
+
+    let exports = this._provider.main;
+    // Let clients find exports on this object.
+    Object.getOwnPropertyNames(exports).forEach(key => {
+      XPCOMUtils.defineLazyGetter(this, key, () => exports[key]);
+    });
+  },
+
+  /**
+   * Choose a default tools provider based on the preferences.
+   */
+  _chooseProvider: function() {
+    if (Services.prefs.prefHasUserValue("devtools.loader.srcdir")) {
+      this.setProvider(SrcdirProvider);
+    } else {
+      this.setProvider(BuiltinProvider);
+    }
+  },
+
+  /**
+   * Reload the current provider.
+   */
+  reload: function() {
+    var events = devtools.require("sdk/system/events");
+    events.emit("startupcache-invalidate", {});
+
+    this._provider.unload("reload");
+    delete this._provider;
+    gDevTools._teardown();
+    this._chooseProvider();
+  },
+};
 
 const FORBIDDEN_IDS = new Set(["toolbox", ""]);
 
 /**
  * DevTools is a class that represents a set of developer tools, it holds a
  * set of tools and keeps track of open toolboxes in the browser.
  */
 this.DevTools = function DevTools() {
@@ -29,21 +202,16 @@ this.DevTools = function DevTools() {
   this._toolboxes = new Map(); // Map<target, toolbox>
 
   // destroy() is an observer's handler so we need to preserve context.
   this.destroy = this.destroy.bind(this);
 
   EventEmitter.decorate(this);
 
   Services.obs.addObserver(this.destroy, "quit-application", false);
-
-  // Register the set of default tools
-  for (let definition of defaultTools) {
-    this.registerTool(definition);
-  }
 }
 
 DevTools.prototype = {
   /**
    * Register a new developer tool.
    *
    * A definition is a light object that holds different information about a
    * developer tool. This object is not supposed to have any operational code.
@@ -102,23 +270,23 @@ DevTools.prototype = {
     this._tools.delete(toolId);
 
     if (!isQuitApplication) {
       this.emit("tool-unregistered", tool);
     }
   },
 
   getDefaultTools: function DT_getDefaultTools() {
-    return defaultTools;
+    return devtools.defaultTools;
   },
 
   getAdditionalTools: function DT_getAdditionalTools() {
     let tools = [];
     for (let [key, value] of this._tools) {
-      if (defaultTools.indexOf(value) == -1) {
+      if (devtools.defaultTools.indexOf(value) == -1) {
         tools.push(value);
       }
     }
     return tools;
   },
 
   /**
    * Allow ToolBoxes to get at the list of tools that they should populate
@@ -211,17 +379,17 @@ DevTools.prototype = {
 
       return promise.then(function() {
         toolbox.raise();
         return toolbox;
       });
     }
     else {
       // No toolbox for target, create one
-      toolbox = new Toolbox(target, toolId, hostType);
+      toolbox = new devtools.Toolbox(target, toolId, hostType);
 
       this._toolboxes.set(target, toolbox);
 
       toolbox.once("destroyed", function() {
         this._toolboxes.delete(target);
         this.emit("toolbox-destroyed", target);
       }.bind(this));
 
@@ -265,16 +433,25 @@ DevTools.prototype = {
     let toolbox = this._toolboxes.get(target);
     if (toolbox == null) {
       return;
     }
     return toolbox.destroy();
   },
 
   /**
+   * Called to tear down a tools provider.
+   */
+  _teardown: function DT_teardown() {
+    for (let [target, toolbox] of this._toolboxes) {
+      toolbox.destroy();
+    }
+  },
+
+  /**
    * All browser windows have been closed, tidy up remaining objects.
    */
   destroy: function() {
     Services.obs.removeObserver(this.destroy, "quit-application");
 
     for (let [key, tool] of this._tools) {
       this.unregisterTool(key, true);
     }
@@ -306,42 +483,50 @@ let gDevToolsBrowser = {
   _trackedBrowserWindows: new Set(),
 
   /**
    * This function is for the benefit of Tools:DevToolbox in
    * browser/base/content/browser-sets.inc and should not be used outside
    * of there
    */
   toggleToolboxCommand: function(gBrowser) {
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
+    let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
     let toolbox = gDevTools.getToolbox(target);
 
     toolbox ? toolbox.destroy() : gDevTools.showToolbox(target);
   },
 
+  toggleBrowserToolboxCommand: function(gBrowser) {
+    let target = devtools.TargetFactory.forWindow(gBrowser.ownerDocument.defaultView);
+    let toolbox = gDevTools.getToolbox(target);
+
+    toolbox ? toolbox.destroy()
+     : gDevTools.showToolbox(target, "inspector", Toolbox.HostType.WINDOW);
+  },
+
   /**
    * This function is for the benefit of Tools:{toolId} commands,
    * triggered from the WebDeveloper menu and keyboard shortcuts.
    *
    * selectToolCommand's behavior:
    * - if the toolbox is closed,
    *   we open the toolbox and select the tool
    * - if the toolbox is open, and the targetted tool is not selected,
    *   we select it
    * - if the toolbox is open, and the targetted tool is selected,
    *   and the host is NOT a window, we close the toolbox
    * - if the toolbox is open, and the targetted tool is selected,
    *   and the host is a window, we raise the toolbox window
    */
   selectToolCommand: function(gBrowser, toolId) {
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
+    let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
     let toolbox = gDevTools.getToolbox(target);
 
     if (toolbox && toolbox.currentToolId == toolId) {
-      if (toolbox.hostType == Toolbox.HostType.WINDOW) {
+      if (toolbox.hostType == devtools.Toolbox.HostType.WINDOW) {
         toolbox.raise();
       } else {
         toolbox.destroy();
       }
     } else {
       gDevTools.showToolbox(target, toolId);
     }
   },
@@ -558,18 +743,18 @@ let gDevToolsBrowser = {
   /**
    * Update the "Toggle Tools" checkbox in the developer tools menu. This is
    * called when a toolbox is created or destroyed.
    */
   _updateMenuCheckbox: function DT_updateMenuCheckbox() {
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
 
       let hasToolbox = false;
-      if (TargetFactory.isKnownTab(win.gBrowser.selectedTab)) {
-        let target = TargetFactory.forTab(win.gBrowser.selectedTab);
+      if (devtools.TargetFactory.isKnownTab(win.gBrowser.selectedTab)) {
+        let target = devtools.TargetFactory.forTab(win.gBrowser.selectedTab);
         if (gDevTools._toolboxes.has(target)) {
           hasToolbox = true;
         }
       }
 
       let broadcaster = win.document.getElementById("devtoolsMenuBroadcaster_DevToolbox");
       if (hasToolbox) {
         broadcaster.setAttribute("checked", "true");
@@ -677,8 +862,11 @@ gDevTools.on("tool-unregistered", functi
   }
   gDevToolsBrowser._removeToolFromWindows(toolId, killswitch);
 });
 
 gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
 gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);
 
 Services.obs.addObserver(gDevToolsBrowser.destroy, "quit-application", false);
+
+// Now load the tools.
+devtools._chooseProvider();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/sidebar.js
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+var Promise = require("sdk/core/promise");
+var EventEmitter = require("devtools/shared/event-emitter");
+
+const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+/**
+ * ToolSidebar provides methods to register tabs in the sidebar.
+ * It's assumed that the sidebar contains a xul:tabbox.
+ *
+ * @param {Node} tabbox
+ *  <tabbox> node;
+ * @param {ToolPanel} panel
+ *  Related ToolPanel instance;
+ * @param {Boolean} showTabstripe
+ *  Show the tabs.
+ */
+function ToolSidebar(tabbox, panel, showTabstripe=true)
+{
+  EventEmitter.decorate(this);
+
+  this._tabbox = tabbox;
+  this._panelDoc = this._tabbox.ownerDocument;
+  this._toolPanel = panel;
+
+  this._tabbox.tabpanels.addEventListener("select", this, true);
+
+  this._tabs = new Map();
+
+  if (!showTabstripe) {
+    this._tabbox.setAttribute("hidetabs", "true");
+  }
+}
+
+exports.ToolSidebar = ToolSidebar;
+
+ToolSidebar.prototype = {
+  /**
+   * Register a tab. A tab is a document.
+   * The document must have a title, which will be used as the name of the tab.
+   *
+   * @param {string} tab uniq id
+   * @param {string} url
+   */
+  addTab: function ToolSidebar_addTab(id, url, selected=false) {
+    let iframe = this._panelDoc.createElementNS(XULNS, "iframe");
+    iframe.className = "iframe-" + id;
+    iframe.setAttribute("flex", "1");
+    iframe.setAttribute("src", url);
+    iframe.tooltip = "aHTMLTooltip";
+
+    let tab = this._tabbox.tabs.appendItem();
+    tab.setAttribute("label", ""); // Avoid showing "undefined" while the tab is loading
+
+    let onIFrameLoaded = function() {
+      tab.setAttribute("label", iframe.contentDocument.title);
+      iframe.removeEventListener("load", onIFrameLoaded, true);
+      if ("setPanel" in iframe.contentWindow) {
+        iframe.contentWindow.setPanel(this._toolPanel, iframe);
+      }
+      this.emit(id + "-ready");
+    }.bind(this);
+
+    iframe.addEventListener("load", onIFrameLoaded, true);
+
+    let tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel");
+    tabpanel.setAttribute("id", "sidebar-panel-" + id);
+    tabpanel.appendChild(iframe);
+    this._tabbox.tabpanels.appendChild(tabpanel);
+
+    this._tooltip = this._panelDoc.createElementNS(XULNS, "tooltip");
+    this._tooltip.id = "aHTMLTooltip";
+    tabpanel.appendChild(this._tooltip);
+    this._tooltip.page = true;
+
+    tab.linkedPanel = "sidebar-panel-" + id;
+
+    // We store the index of this tab.
+    this._tabs.set(id, tab);
+
+    if (selected) {
+      // For some reason I don't understand, if we call this.select in this
+      // event loop (after inserting the tab), the tab will never get the
+      // the "selected" attribute set to true.
+      this._panelDoc.defaultView.setTimeout(function() {
+        this.select(id);
+      }.bind(this), 10);
+    }
+
+    this.emit("new-tab-registered", id);
+  },
+
+  /**
+   * Select a specific tab.
+   */
+  select: function ToolSidebar_select(id) {
+    let tab = this._tabs.get(id);
+    if (tab) {
+      this._tabbox.selectedTab = tab;
+    }
+  },
+
+  /**
+   * Return the id of the selected tab.
+   */
+  getCurrentTabID: function ToolSidebar_getCurrentTabID() {
+    let currentID = null;
+    for (let [id, tab] of this._tabs) {
+      if (this._tabbox.tabs.selectedItem == tab) {
+        currentID = id;
+        break;
+      }
+    }
+    return currentID;
+  },
+
+  /**
+   * Returns the requested tab based on the id.
+   *
+   * @param String id
+   *        unique id of the requested tab.
+   */
+  getTab: function ToolSidebar_getTab(id) {
+    return this._tabbox.tabpanels.querySelector("#sidebar-panel-" + id);
+  },
+
+  /**
+   * Event handler.
+   */
+  handleEvent: function ToolSidebar_eventHandler(event) {
+    if (event.type == "select") {
+      let previousTool = this._currentTool;
+      this._currentTool = this.getCurrentTabID();
+      if (previousTool) {
+        this.emit(previousTool + "-unselected");
+      }
+
+      this.emit(this._currentTool + "-selected");
+      this.emit("select", this._currentTool);
+    }
+  },
+
+  /**
+   * Toggle sidebar's visibility state.
+   */
+  toggle: function ToolSidebar_toggle() {
+    if (this._tabbox.hasAttribute("hidden")) {
+      this.show();
+    } else {
+      this.hide();
+    }
+  },
+
+  /**
+   * Show the sidebar.
+   */
+  show: function ToolSidebar_show() {
+    this._tabbox.removeAttribute("hidden");
+  },
+
+  /**
+   * Show the sidebar.
+   */
+  hide: function ToolSidebar_hide() {
+    this._tabbox.setAttribute("hidden", "true");
+  },
+
+  /**
+   * Return the window containing the tab content.
+   */
+  getWindowForTab: function ToolSidebar_getWindowForTab(id) {
+    if (!this._tabs.has(id)) {
+      return null;
+    }
+
+    let panel = this._panelDoc.getElementById(this._tabs.get(id).linkedPanel);
+    return panel.firstChild.contentWindow;
+  },
+
+  /**
+   * Clean-up.
+   */
+  destroy: function ToolSidebar_destroy() {
+    if (this._destroyed) {
+      return Promise.resolve(null);
+    }
+    this._destroyed = true;
+
+    this._tabbox.tabpanels.removeEventListener("select", this, true);
+
+    while (this._tabbox.tabpanels.hasChildNodes()) {
+      this._tabbox.tabpanels.removeChild(this._tabbox.tabpanels.firstChild);
+    }
+
+    while (this._tabbox.tabs.hasChildNodes()) {
+      this._tabbox.tabs.removeChild(this._tabbox.tabs.firstChild);
+    }
+
+    this._tabs = null;
+    this._tabbox = null;
+    this._panelDoc = null;
+    this._toolPanel = null;
+
+    return Promise.resolve(null);
+  },
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/target.js
@@ -0,0 +1,595 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc, Ci, Cu} = require("chrome");
+
+var Promise = require("sdk/core/promise");
+var EventEmitter = require("devtools/shared/event-emitter");
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
+  "resource://gre/modules/devtools/dbg-server.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
+  "resource://gre/modules/devtools/dbg-client.jsm");
+
+const targets = new WeakMap();
+const promiseTargets = new WeakMap();
+
+/**
+ * Functions for creating Targets
+ */
+exports.TargetFactory = {
+  /**
+   * Construct a Target
+   * @param {XULTab} tab
+   *        The tab to use in creating a new target.
+   *
+   * @return A target object
+   */
+  forTab: function TF_forTab(tab) {
+    let target = targets.get(tab);
+    if (target == null) {
+      target = new TabTarget(tab);
+      targets.set(tab, target);
+    }
+    return target;
+  },
+
+  /**
+   * Return a promise of a Target for a remote tab.
+   * @param {Object} options
+   *        The options object has the following properties:
+   *        {
+   *          form: the remote protocol form of a tab,
+   *          client: a DebuggerClient instance,
+   *          chrome: true if the remote target is the whole process
+   *        }
+   *
+   * @return A promise of a target object
+   */
+  forRemoteTab: function TF_forRemoteTab(options) {
+    let promise = promiseTargets.get(options);
+    if (promise == null) {
+      let target = new TabTarget(options);
+      promise = target.makeRemote().then(() => target);
+      promiseTargets.set(options, promise);
+    }
+    return promise;
+  },
+
+  /**
+   * Creating a target for a tab that is being closed is a problem because it
+   * allows a leak as a result of coming after the close event which normally
+   * clears things up. This function allows us to ask if there is a known
+   * target for a tab without creating a target
+   * @return true/false
+   */
+  isKnownTab: function TF_isKnownTab(tab) {
+    return targets.has(tab);
+  },
+
+  /**
+   * Construct a Target
+   * @param {nsIDOMWindow} window
+   *        The chromeWindow to use in creating a new target
+   * @return A target object
+   */
+  forWindow: function TF_forWindow(window) {
+    let target = targets.get(window);
+    if (target == null) {
+      target = new WindowTarget(window);
+      targets.set(window, target);
+    }
+    return target;
+  },
+
+  /**
+   * Get all of the targets known to the local browser instance
+   * @return An array of target objects
+   */
+  allTargets: function TF_allTargets() {
+    let windows = [];
+    let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+                       .getService(Ci.nsIWindowMediator);
+    let en = wm.getXULWindowEnumerator(null);
+    while (en.hasMoreElements()) {
+      windows.push(en.getNext());
+    }
+
+    return windows.map(function(window) {
+      return TargetFactory.forWindow(window);
+    });
+  },
+};
+
+/**
+ * The 'version' property allows the developer tools equivalent of browser
+ * detection. Browser detection is evil, however while we don't know what we
+ * will need to detect in the future, it is an easy way to postpone work.
+ * We should be looking to use 'supports()' in place of version where
+ * possible.
+ */
+function getVersion() {
+  // FIXME: return something better
+  return 20;
+}
+
+/**
+ * A better way to support feature detection, but we're not yet at a place
+ * where we have the features well enough defined for this to make lots of
+ * sense.
+ */
+function supports(feature) {
+  // FIXME: return something better
+  return false;
+};
+
+/**
+ * A Target represents something that we can debug. Targets are generally
+ * read-only. Any changes that you wish to make to a target should be done via
+ * a Tool that attaches to the target. i.e. a Target is just a pointer saying
+ * "the thing to debug is over there".
+ *
+ * Providing a generalized abstraction of a web-page or web-browser (available
+ * either locally or remotely) is beyond the scope of this class (and maybe
+ * also beyond the scope of this universe) However Target does attempt to
+ * abstract some common events and read-only properties common to many Tools.
+ *
+ * Supported read-only properties:
+ * - name, isRemote, url
+ *
+ * Target extends EventEmitter and provides support for the following events:
+ * - close: The target window has been closed. All tools attached to this
+ *     target should close. This event is not currently cancelable.
+ * - navigate: The target window has navigated to a different URL
+ *
+ * Optional events:
+ * - will-navigate: The target window will navigate to a different URL
+ * - hidden: The target is not visible anymore (for TargetTab, another tab is selected)
+ * - visible: The target is visible (for TargetTab, tab is selected)
+ *
+ * Target also supports 2 functions to help allow 2 different versions of
+ * Firefox debug each other. The 'version' property is the equivalent of
+ * browser detection - simple and easy to implement but gets fragile when things
+ * are not quite what they seem. The 'supports' property is the equivalent of
+ * feature detection - harder to setup, but more robust long-term.
+ *
+ * Comparing Targets: 2 instances of a Target object can point at the same
+ * thing, so t1 !== t2 and t1 != t2 even when they represent the same object.
+ * To compare to targets use 't1.equals(t2)'.
+ */
+function Target() {
+  throw new Error("Use TargetFactory.newXXX or Target.getXXX to create a Target in place of 'new Target()'");
+}
+
+Object.defineProperty(Target.prototype, "version", {
+  get: getVersion,
+  enumerable: true
+});
+
+
+/**
+ * A TabTarget represents a page living in a browser tab. Generally these will
+ * be web pages served over http(s), but they don't have to be.
+ */
+function TabTarget(tab) {
+  EventEmitter.decorate(this);
+  this.destroy = this.destroy.bind(this);
+  this._handleThreadState = this._handleThreadState.bind(this);
+  this.on("thread-resumed", this._handleThreadState);
+  this.on("thread-paused", this._handleThreadState);
+  // Only real tabs need initialization here. Placeholder objects for remote
+  // targets will be initialized after a makeRemote method call.
+  if (tab && !["client", "form", "chrome"].every(tab.hasOwnProperty, tab)) {
+    this._tab = tab;
+    this._setupListeners();
+  } else {
+    this._form = tab.form;
+    this._client = tab.client;
+    this._chrome = tab.chrome;
+  }
+}
+
+TabTarget.prototype = {
+  _webProgressListener: null,
+
+  supports: supports,
+  get version() { return getVersion(); },
+
+  get tab() {
+    return this._tab;
+  },
+
+  get form() {
+    return this._form;
+  },
+
+  get client() {
+    return this._client;
+  },
+
+  get chrome() {
+    return this._chrome;
+  },
+
+  get window() {
+    // Be extra careful here, since this may be called by HS_getHudByWindow
+    // during shutdown.
+    if (this._tab && this._tab.linkedBrowser) {
+      return this._tab.linkedBrowser.contentWindow;
+    }
+  },
+
+  get name() {
+    return this._tab ? this._tab.linkedBrowser.contentDocument.title :
+                       this._form.title;
+  },
+
+  get url() {
+    return this._tab ? this._tab.linkedBrowser.contentDocument.location.href :
+                       this._form.url;
+  },
+
+  get isRemote() {
+    return !this.isLocalTab;
+  },
+
+  get isLocalTab() {
+    return !!this._tab;
+  },
+
+  get isThreadPaused() {
+    return !!this._isThreadPaused;
+  },
+
+  /**
+   * Adds remote protocol capabilities to the target, so that it can be used
+   * for tools that support the Remote Debugging Protocol even for local
+   * connections.
+   */
+  makeRemote: function TabTarget_makeRemote() {
+    if (this._remote) {
+      return this._remote.promise;
+    }
+
+    this._remote = Promise.defer();
+
+    if (this.isLocalTab) {
+      // Since a remote protocol connection will be made, let's start the
+      // DebuggerServer here, once and for all tools.
+      if (!DebuggerServer.initialized) {
+        DebuggerServer.init();
+        DebuggerServer.addBrowserActors();
+      }
+
+      this._client = new DebuggerClient(DebuggerServer.connectPipe());
+      // A local TabTarget will never perform chrome debugging.
+      this._chrome = false;
+    }
+
+    this._setupRemoteListeners();
+
+    let attachTab = () => {
+      this._client.attachTab(this._form.actor, (aResponse, aTabClient) => {
+        if (!aTabClient) {
+          this._remote.reject("Unable to attach to the tab");
+          return;
+        }
+        this.threadActor = aResponse.threadActor;
+        this._remote.resolve(null);
+      });
+    };
+
+    if (this.isLocalTab) {
+      this._client.connect((aType, aTraits) => {
+        this._client.listTabs(aResponse => {
+          this._form = aResponse.tabs[aResponse.selected];
+          attachTab();
+        });
+      });
+    } else if (!this.chrome) {
+      // In the remote debugging case, the protocol connection will have been
+      // already initialized in the connection screen code.
+      attachTab();
+    } else {
+      // Remote chrome debugging doesn't need anything at this point.
+      this._remote.resolve(null);
+    }
+
+    return this._remote.promise;
+  },
+
+  /**
+   * Listen to the different events.
+   */
+  _setupListeners: function TabTarget__setupListeners() {
+    this._webProgressListener = new TabWebProgressListener(this);
+    this.tab.linkedBrowser.addProgressListener(this._webProgressListener);
+    this.tab.addEventListener("TabClose", this);
+    this.tab.parentNode.addEventListener("TabSelect", this);
+    this.tab.ownerDocument.defaultView.addEventListener("unload", this);
+  },
+
+  /**
+   * Setup listeners for remote debugging, updating existing ones as necessary.
+   */
+  _setupRemoteListeners: function TabTarget__setupRemoteListeners() {
+    this.client.addListener("tabDetached", this.destroy);
+
+    this._onTabNavigated = function onRemoteTabNavigated(aType, aPacket) {
+      let event = Object.create(null);
+      event.url = aPacket.url;
+      event.title = aPacket.title;
+      event.nativeConsoleAPI = aPacket.nativeConsoleAPI;
+      // Send any stored event payload (DOMWindow or nsIRequest) for backwards
+      // compatibility with non-remotable tools.
+      if (aPacket.state == "start") {
+        event._navPayload = this._navRequest;
+        this.emit("will-navigate", event);
+        this._navRequest = null;
+      } else {
+        event._navPayload = this._navWindow;
+        this.emit("navigate", event);
+        this._navWindow = null;
+      }
+    }.bind(this);
+    this.client.addListener("tabNavigated", this._onTabNavigated);
+  },
+
+  /**
+   * Handle tabs events.
+   */
+  handleEvent: function (event) {
+    switch (event.type) {
+      case "TabClose":
+      case "unload":
+        this.destroy();
+        break;
+      case "TabSelect":
+        if (this.tab.selected) {
+          this.emit("visible", event);
+        } else {
+          this.emit("hidden", event);
+        }
+        break;
+    }
+  },
+
+  /**
+   * Handle script status.
+   */
+  _handleThreadState: function(event) {
+    switch (event) {
+      case "thread-resumed":
+        this._isThreadPaused = false;
+        break;
+      case "thread-paused":
+        this._isThreadPaused = true;
+        break;
+    }
+  },
+
+  /**
+   * Target is not alive anymore.
+   */
+  destroy: function() {
+    // If several things call destroy then we give them all the same
+    // destruction promise so we're sure to destroy only once
+    if (this._destroyer) {
+      return this._destroyer.promise;
+    }
+
+    this._destroyer = Promise.defer();
+
+    // Before taking any action, notify listeners that destruction is imminent.
+    this.emit("close");
+
+    // First of all, do cleanup tasks that pertain to both remoted and
+    // non-remoted targets.
+    this.off("thread-resumed", this._handleThreadState);
+    this.off("thread-paused", this._handleThreadState);
+
+    if (this._tab) {
+      if (this._webProgressListener) {
+        this._webProgressListener.destroy();
+      }
+
+      this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
+      this._tab.removeEventListener("TabClose", this);
+      this._tab.parentNode.removeEventListener("TabSelect", this);
+    }
+
+    // If this target was not remoted, the promise will be resolved before the
+    // function returns.
+    if (this._tab && !this._client) {
+      targets.delete(this._tab);
+      this._tab = null;
+      this._client = null;
+      this._form = null;
+      this._remote = null;
+
+      this._destroyer.resolve(null);
+    } else if (this._client) {
+      // If, on the other hand, this target was remoted, the promise will be
+      // resolved after the remote connection is closed.
+      this.client.removeListener("tabNavigated", this._onTabNavigated);
+      this.client.removeListener("tabDetached", this.destroy);
+
+      this._client.close(function onClosed() {
+        if (this._tab) {
+          targets.delete(this._tab);
+        } else {
+          promiseTargets.delete(this._form);
+        }
+        this._client = null;
+        this._tab = null;
+        this._form = null;
+        this._remote = null;
+
+        this._destroyer.resolve(null);
+      }.bind(this));
+    }
+
+    return this._destroyer.promise;
+  },
+
+  toString: function() {
+    return 'TabTarget:' + (this._tab ? this._tab : (this._form && this._form.actor));
+  },
+};
+
+
+/**
+ * WebProgressListener for TabTarget.
+ *
+ * @param object aTarget
+ *        The TabTarget instance to work with.
+ */
+function TabWebProgressListener(aTarget) {
+  this.target = aTarget;
+}
+
+TabWebProgressListener.prototype = {
+  target: null,
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
+
+  onStateChange: function TWPL_onStateChange(progress, request, flag, status) {
+    let isStart = flag & Ci.nsIWebProgressListener.STATE_START;
+    let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
+    let isNetwork = flag & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
+    let isRequest = flag & Ci.nsIWebProgressListener.STATE_IS_REQUEST;
+
+    // Skip non-interesting states.
+    if (!isStart || !isDocument || !isRequest || !isNetwork) {
+      return;
+    }
+
+    // emit event if the top frame is navigating
+    if (this.target && this.target.window == progress.DOMWindow) {
+      // Emit the event if the target is not remoted or store the payload for
+      // later emission otherwise.
+      if (this.target._client) {
+        this.target._navRequest = request;
+      } else {
+        this.target.emit("will-navigate", request);
+      }
+    }
+  },
+
+  onProgressChange: function() {},
+  onSecurityChange: function() {},
+  onStatusChange: function() {},
+
+  onLocationChange: function TWPL_onLocationChange(webProgress, request, URI, flags) {
+    if (this.target &&
+        !(flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
+      let window = webProgress.DOMWindow;
+      // Emit the event if the target is not remoted or store the payload for
+      // later emission otherwise.
+      if (this.target._client) {
+        this.target._navWindow = window;
+      } else {
+        this.target.emit("navigate", window);
+      }
+    }
+  },
+
+  /**
+   * Destroy the progress listener instance.
+   */
+  destroy: function TWPL_destroy() {
+    if (this.target.tab) {
+      this.target.tab.linkedBrowser.removeProgressListener(this);
+    }
+    this.target._webProgressListener = null;
+    this.target._navRequest = null;
+    this.target._navWindow = null;
+    this.target = null;
+  }
+};
+
+
+/**
+ * A WindowTarget represents a page living in a xul window or panel. Generally
+ * these will have a chrome: URL
+ */
+function WindowTarget(window) {
+  EventEmitter.decorate(this);
+  this._window = window;
+  this._setupListeners();
+}
+
+WindowTarget.prototype = {
+  supports: supports,
+  get version() { return getVersion(); },
+
+  get window() {
+    return this._window;
+  },
+
+  get name() {
+    return this._window.document.title;
+  },
+
+  get url() {
+    return this._window.document.location.href;
+  },
+
+  get isRemote() {
+    return false;
+  },
+
+  get isLocalTab() {
+    return false;
+  },
+
+  get isThreadPaused() {
+    return !!this._isThreadPaused;
+  },
+
+  /**
+   * Listen to the different events.
+   */
+  _setupListeners: function() {
+    this._handleThreadState = this._handleThreadState.bind(this);
+    this.on("thread-paused", this._handleThreadState);
+    this.on("thread-resumed", this._handleThreadState);
+  },
+
+  _handleThreadState: function(event) {
+    switch (event) {
+      case "thread-resumed":
+        this._isThreadPaused = false;
+        break;
+      case "thread-paused":
+        this._isThreadPaused = true;
+        break;
+    }
+  },
+
+  /**
+   * Target is not alive anymore.
+   */
+  destroy: function() {
+    if (!this._destroyed) {
+      this._destroyed = true;
+
+      this.off("thread-paused", this._handleThreadState);
+      this.off("thread-resumed", this._handleThreadState);
+      this.emit("close");
+
+      targets.delete(this._window);
+      this._window = null;
+    }
+
+    return Promise.resolve(null);
+  },
+
+  toString: function() {
+    return 'WindowTarget:' + this.window;
+  },
+};
--- a/browser/devtools/framework/test/browser_devtools_api.js
+++ b/browser/devtools/framework/test/browser_devtools_api.js
@@ -2,20 +2,18 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests devtools API
 
 const Cu = Components.utils;
 const toolId = "test-tool";
 
 let tempScope = {};
-Cu.import("resource:///modules/devtools/EventEmitter.jsm", tempScope);
+Cu.import("resource:///modules/devtools/shared/event-emitter.js", tempScope);
 let EventEmitter = tempScope.EventEmitter;
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
 
 function test() {
   addTab("about:blank", function(aBrowser, aTab) {
     runTests(aTab);
   });
 }
 
 function runTests(aTab) {
--- a/browser/devtools/framework/test/browser_new_activation_workflow.js
+++ b/browser/devtools/framework/test/browser_new_activation_workflow.js
@@ -3,18 +3,16 @@
 
 // Tests devtools API
 
 const Cu = Components.utils;
 
 let toolbox, target;
 
 let tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
 
 function test() {
   addTab("about:blank", function(aBrowser, aTab) {
     target = TargetFactory.forTab(gBrowser.selectedTab);
     loadWebConsole(aTab).then(function() {
       console.log('loaded');
     }, console.error);
   });
--- a/browser/devtools/framework/test/browser_target_events.js
+++ b/browser/devtools/framework/test/browser_target_events.js
@@ -1,16 +1,12 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-var TargetFactory = tempScope.TargetFactory;
-
 var target;
 
 function test()
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", onLoad, true);
--- a/browser/devtools/framework/test/browser_toolbox_dynamic_registration.js
+++ b/browser/devtools/framework/test/browser_toolbox_dynamic_registration.js
@@ -1,18 +1,14 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let toolbox;
 
-let temp = {};
-Cu.import("resource:///modules/devtools/Target.jsm", temp);
-let TargetFactory = temp.TargetFactory;
-
 function test()
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
--- a/browser/devtools/framework/test/browser_toolbox_hosts.js
+++ b/browser/devtools/framework/test/browser_toolbox_hosts.js
@@ -1,21 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let temp = {}
 Cu.import("resource:///modules/devtools/gDevTools.jsm", temp);
 let DevTools = temp.DevTools;
 
-Cu.import("resource:///modules/devtools/Toolbox.jsm", temp);
-let Toolbox = temp.Toolbox;
-
-Cu.import("resource:///modules/devtools/Target.jsm", temp);
-let TargetFactory = temp.TargetFactory;
+let Toolbox = devtools.Toolbox;
 
 let toolbox, target;
 
 function test()
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
--- a/browser/devtools/framework/test/browser_toolbox_options.js
+++ b/browser/devtools/framework/test/browser_toolbox_options.js
@@ -1,14 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
 let doc = null, toolbox = null, panelWin = null, index = 0, prefValues = [], prefNodes = [];
 
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
--- a/browser/devtools/framework/test/browser_toolbox_ready.js
+++ b/browser/devtools/framework/test/browser_toolbox_ready.js
@@ -1,16 +1,12 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
-
 function test()
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
--- a/browser/devtools/framework/test/browser_toolbox_sidebar.js
+++ b/browser/devtools/framework/test/browser_toolbox_sidebar.js
@@ -1,18 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   const Cu = Components.utils;
-  let tempScope = {};
-  Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
-  Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-  Cu.import("resource:///modules/devtools/Sidebar.jsm", tempScope);
-  let {TargetFactory: TargetFactory, gDevTools: gDevTools, ToolSidebar: ToolSidebar} = tempScope;
+  let {ToolSidebar} = devtools.require("devtools/framework/sidebar");
 
   const toolURL = "data:text/xml;charset=utf8,<?xml version='1.0'?>" +
                   "<?xml-stylesheet href='chrome://browser/skin/devtools/common.css' type='text/css'?>" +
                   "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'>" +
                   "<hbox flex='1'><description flex='1'>foo</description><splitter class='devtools-side-splitter'/>" +
                   "<tabbox flex='1' id='sidebar' class='devtools-sidebar-tabs'><tabs/><tabpanels flex='1'/></tabbox>" +
                   "</hbox>" +
                   "</window>";
--- a/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
@@ -1,15 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let temp = {};
-Cu.import("resource:///modules/devtools/Toolbox.jsm", temp);
-let Toolbox = temp.Toolbox;
-temp = null;
+let Toolbox = devtools.Toolbox;
 
 let toolbox, toolIDs, idIndex;
 
 function test() {
   waitForExplicitFinish();
 
   if (window.navigator.userAgent.indexOf("Mac OS X 10.8") != -1 ||
       window.navigator.userAgent.indexOf("Windows NT 5.1") != -1) {
--- a/browser/devtools/framework/test/browser_toolbox_window_title_changes.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_title_changes.js
@@ -1,21 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+let Toolbox = devtools.Toolbox;
 let temp = {};
-Cu.import("resource:///modules/devtools/Toolbox.jsm", temp);
-let Toolbox = temp.Toolbox;
-temp = {};
-Cu.import("resource:///modules/devtools/Target.jsm", temp);
-let TargetFactory = temp.TargetFactory;
-temp = {};
-Cu.import("resource:///modules/devtools/gDevTools.jsm", temp);
-let gDevTools = temp.gDevTools;
-temp = {};
 Cu.import("resource://gre/modules/Services.jsm", temp);
 let Services = temp.Services;
 temp = null;
 
 function test() {
   waitForExplicitFinish();
 
   const URL_1 = "data:text/plain;charset=UTF-8,abcde";
--- a/browser/devtools/framework/test/head.js
+++ b/browser/devtools/framework/test/head.js
@@ -1,20 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+let TargetFactory = gDevTools.TargetFactory;
+
 let tempScope = {};
-Components.utils.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
 Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
 let console = tempScope.console;
 Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js", tempScope);
 let Promise = tempScope.Promise;
 
+let {devtools} = Components.utils.import("resource:///modules/devtools/gDevTools.jsm", {});
+let TargetFactory = devtools.TargetFactory;
+
 /**
  * Open a new tab at a URL and call a callback on load
  */
 function addTab(aURL, aCallback)
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/toolbox-hosts.js
@@ -0,0 +1,282 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cu} = require("chrome");
+
+let Promise = require("sdk/core/promise");
+let EventEmitter = require("devtools/shared/event-emitter");
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+/**
+ * A toolbox host represents an object that contains a toolbox (e.g. the
+ * sidebar or a separate window). Any host object should implement the
+ * following functions:
+ *
+ * create() - create the UI and emit a 'ready' event when the UI is ready to use
+ * destroy() - destroy the host's UI
+ */
+
+exports.Hosts = {
+  "bottom": BottomHost,
+  "side": SidebarHost,
+  "window": WindowHost
+}
+
+/**
+ * Host object for the dock on the bottom of the browser
+ */
+function BottomHost(hostTab) {
+  this.hostTab = hostTab;
+
+  EventEmitter.decorate(this);
+}
+
+BottomHost.prototype = {
+  type: "bottom",
+
+  heightPref: "devtools.toolbox.footer.height",
+
+  /**
+   * Create a box at the bottom of the host tab.
+   */
+  create: function BH_create() {
+    let deferred = Promise.defer();
+
+    let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
+    let ownerDocument = gBrowser.ownerDocument;
+
+    this._splitter = ownerDocument.createElement("splitter");
+    this._splitter.setAttribute("class", "devtools-horizontal-splitter");
+
+    this.frame = ownerDocument.createElement("iframe");
+    this.frame.className = "devtools-toolbox-bottom-iframe";
+    this.frame.height = Services.prefs.getIntPref(this.heightPref);
+
+    this._nbox = gBrowser.getNotificationBox(this.hostTab.linkedBrowser);
+    this._nbox.appendChild(this._splitter);
+    this._nbox.appendChild(this.frame);
+
+    let frameLoad = function() {
+      this.frame.removeEventListener("DOMContentLoaded", frameLoad, true);
+      this.emit("ready", this.frame);
+
+      deferred.resolve(this.frame);
+    }.bind(this);
+
+    this.frame.tooltip = "aHTMLTooltip";
+    this.frame.addEventListener("DOMContentLoaded", frameLoad, true);
+
+    // we have to load something so we can switch documents if we have to
+    this.frame.setAttribute("src", "about:blank");
+
+    focusTab(this.hostTab);
+
+    return deferred.promise;
+  },
+
+  /**
+   * Raise the host.
+   */
+  raise: function BH_raise() {
+    focusTab(this.hostTab);
+  },
+
+  /**
+   * Set the toolbox title.
+   */
+  setTitle: function BH_setTitle(title) {
+    // Nothing to do for this host type.
+  },
+
+  /**
+   * Destroy the bottom dock.
+   */
+  destroy: function BH_destroy() {
+    if (!this._destroyed) {
+      this._destroyed = true;
+
+      Services.prefs.setIntPref(this.heightPref, this.frame.height);
+      this._nbox.removeChild(this._splitter);
+      this._nbox.removeChild(this.frame);
+    }
+
+    return Promise.resolve(null);
+  }
+}
+
+
+/**
+ * Host object for the in-browser sidebar
+ */
+function SidebarHost(hostTab) {
+  this.hostTab = hostTab;
+
+  EventEmitter.decorate(this);
+}
+
+SidebarHost.prototype = {
+  type: "side",
+
+  widthPref: "devtools.toolbox.sidebar.width",
+
+  /**
+   * Create a box in the sidebar of the host tab.
+   */
+  create: function SH_create() {
+    let deferred = Promise.defer();
+
+    let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
+    let ownerDocument = gBrowser.ownerDocument;
+
+    this._splitter = ownerDocument.createElement("splitter");
+    this._splitter.setAttribute("class", "devtools-side-splitter");
+
+    this.frame = ownerDocument.createElement("iframe");
+    this.frame.className = "devtools-toolbox-side-iframe";
+    this.frame.width = Services.prefs.getIntPref(this.widthPref);
+
+    this._sidebar = gBrowser.getSidebarContainer(this.hostTab.linkedBrowser);
+    this._sidebar.appendChild(this._splitter);
+    this._sidebar.appendChild(this.frame);
+
+    let frameLoad = function() {
+      this.frame.removeEventListener("DOMContentLoaded", frameLoad, true);
+      this.emit("ready", this.frame);
+
+      deferred.resolve(this.frame);
+    }.bind(this);
+
+    this.frame.addEventListener("DOMContentLoaded", frameLoad, true);
+    this.frame.tooltip = "aHTMLTooltip";
+    this.frame.setAttribute("src", "about:blank");
+
+    focusTab(this.hostTab);
+
+    return deferred.promise;
+  },
+
+  /**
+   * Raise the host.
+   */
+  raise: function SH_raise() {
+    focusTab(this.hostTab);
+  },
+
+  /**
+   * Set the toolbox title.
+   */
+  setTitle: function SH_setTitle(title) {
+    // Nothing to do for this host type.
+  },
+
+  /**
+   * Destroy the sidebar.
+   */
+  destroy: function SH_destroy() {
+    if (!this._destroyed) {
+      this._destroyed = true;
+
+      Services.prefs.setIntPref(this.widthPref, this.frame.width);
+      this._sidebar.removeChild(this._splitter);
+      this._sidebar.removeChild(this.frame);
+    }
+
+    return Promise.resolve(null);
+  }
+}
+
+/**
+ * Host object for the toolbox in a separate window
+ */
+function WindowHost() {
+  this._boundUnload = this._boundUnload.bind(this);
+
+  EventEmitter.decorate(this);
+}
+
+WindowHost.prototype = {
+  type: "window",
+
+  WINDOW_URL: "chrome://browser/content/devtools/framework/toolbox-window.xul",
+
+  /**
+   * Create a new xul window to contain the toolbox.
+   */
+  create: function WH_create() {
+    let deferred = Promise.defer();
+
+    let flags = "chrome,centerscreen,resizable,dialog=no";
+    let win = Services.ww.openWindow(null, this.WINDOW_URL, "_blank",
+                                     flags, null);
+
+    let frameLoad = function(event) {
+      win.removeEventListener("load", frameLoad, true);
+      this.frame = win.document.getElementById("toolbox-iframe");
+      this.emit("ready", this.frame);
+
+      deferred.resolve(this.frame);
+    }.bind(this);
+
+    win.addEventListener("load", frameLoad, true);
+    win.addEventListener("unload", this._boundUnload);
+
+    win.focus();
+
+    this._window = win;
+
+    return deferred.promise;
+  },
+
+  /**
+   * Catch the user closing the window.
+   */
+  _boundUnload: function(event) {
+    if (event.target.location != this.WINDOW_URL) {
+      return;
+    }
+    this._window.removeEventListener("unload", this._boundUnload);
+
+    this.emit("window-closed");
+  },
+
+  /**
+   * Raise the host.
+   */
+  raise: function RH_raise() {
+    this._window.focus();
+  },
+
+  /**
+   * Set the toolbox title.
+   */
+  setTitle: function WH_setTitle(title) {
+    this._window.document.title = title;
+  },
+
+  /**
+   * Destroy the window.
+   */
+  destroy: function WH_destroy() {
+    if (!this._destroyed) {
+      this._destroyed = true;
+
+      this._window.removeEventListener("unload", this._boundUnload);
+      this._window.close();
+    }
+
+    return Promise.resolve(null);
+  }
+}
+
+/**
+ *  Switch to the given tab in a browser and focus the browser window
+ */
+function focusTab(tab) {
+  let browserWindow = tab.ownerDocument.defaultView;
+  browserWindow.focus();
+  browserWindow.gBrowser.selectedTab = tab;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/toolbox.js
@@ -0,0 +1,722 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc, Ci, Cu} = require("chrome");
+
+let Promise = require("sdk/core/promise");
+let EventEmitter = require("devtools/shared/event-emitter");
+
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource:///modules/devtools/gDevTools.jsm");
+
+loader.lazyGetter(this, "Hosts", () => require("devtools/framework/toolbox-hosts").Hosts);
+
+XPCOMUtils.defineLazyModuleGetter(this, "CommandUtils",
+                                  "resource:///modules/devtools/DeveloperToolbar.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "toolboxStrings", function() {
+  let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
+  let l10n = function(aName, ...aArgs) {
+    try {
+      if (aArgs.length == 0) {
+        return bundle.GetStringFromName(aName);
+      } else {
+        return bundle.formatStringFromName(aName, aArgs, aArgs.length);
+      }
+    } catch (ex) {
+      Services.console.logStringMessage("Error reading '" + aName + "'");
+    }
+  };
+  return l10n;
+});
+
+XPCOMUtils.defineLazyGetter(this, "Requisition", function() {
+  let scope = {};
+  Cu.import("resource://gre/modules/devtools/Require.jsm", scope);
+  Cu.import("resource:///modules/devtools/gcli.jsm", scope);
+
+  let req = scope.require;
+  return req('gcli/cli').Requisition;
+});
+
+/**
+ * A "Toolbox" is the component that holds all the tools for one specific
+ * target. Visually, it's a document that includes the tools tabs and all
+ * the iframes where the tool panels will be living in.
+ *
+ * @param {object} target
+ *        The object the toolbox is debugging.
+ * @param {string} selectedTool
+ *        Tool to select initially
+ * @param {Toolbox.HostType} hostType
+ *        Type of host that will host the toolbox (e.g. sidebar, window)
+ */
+function Toolbox(target, selectedTool, hostType) {
+  this._target = target;
+  this._toolPanels = new Map();
+
+  this._toolRegistered = this._toolRegistered.bind(this);
+  this._toolUnregistered = this._toolUnregistered.bind(this);
+  this.destroy = this.destroy.bind(this);
+
+  this._target.on("close", this.destroy);
+
+  if (!hostType) {
+    hostType = Services.prefs.getCharPref(this._prefs.LAST_HOST);
+  }
+  if (!selectedTool) {
+    selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
+  }
+  let definitions = gDevTools.getToolDefinitionMap();
+  if (!definitions.get(selectedTool) && selectedTool != "options") {
+    selectedTool = "webconsole";
+  }
+  this._defaultToolId = selectedTool;
+
+  this._host = this._createHost(hostType);
+
+  EventEmitter.decorate(this);
+
+  this._refreshHostTitle = this._refreshHostTitle.bind(this);
+  this._target.on("navigate", this._refreshHostTitle);
+  this.on("host-changed", this._refreshHostTitle);
+  this.on("select", this._refreshHostTitle);
+
+  gDevTools.on("tool-registered", this._toolRegistered);
+  gDevTools.on("tool-unregistered", this._toolUnregistered);
+}
+exports.Toolbox = Toolbox;
+
+/**
+ * The toolbox can be 'hosted' either embedded in a browser window
+ * or in a separate window.
+ */
+Toolbox.HostType = {
+  BOTTOM: "bottom",
+  SIDE: "side",
+  WINDOW: "window"
+}
+
+Toolbox.prototype = {
+  _URL: "chrome://browser/content/devtools/framework/toolbox.xul",
+
+  _prefs: {
+    LAST_HOST: "devtools.toolbox.host",
+    LAST_TOOL: "devtools.toolbox.selectedTool",
+    SIDE_ENABLED: "devtools.toolbox.sideEnabled"
+  },
+
+  HostType: Toolbox.HostType,
+
+  /**
+   * Returns a *copy* of the _toolPanels collection.
+   *
+   * @return {Map} panels
+   *         All the running panels in the toolbox
+   */
+  getToolPanels: function TB_getToolPanels() {
+    let panels = new Map();
+
+    for (let [key, value] of this._toolPanels) {
+      panels.set(key, value);
+    }
+    return panels;
+  },
+
+  /**
+   * Access the panel for a given tool
+   */
+  getPanel: function TBOX_getPanel(id) {
+    return this.getToolPanels().get(id);
+  },
+
+  /**
+   * This is a shortcut for getPanel(currentToolId) because it is much more
+   * likely that we're going to want to get the panel that we've just made
+   * visible
+   */
+  getCurrentPanel: function TBOX_getCurrentPanel() {
+    return this.getToolPanels().get(this.currentToolId);
+  },
+
+  /**
+   * Get/alter the target of a Toolbox so we're debugging something different.
+   * See Target.jsm for more details.
+   * TODO: Do we allow |toolbox.target = null;| ?
+   */
+  get target() {
+    return this._target;
+  },
+
+  /**
+   * Get/alter the host of a Toolbox, i.e. is it in browser or in a separate
+   * tab. See HostType for more details.
+   */
+  get hostType() {
+    return this._host.type;
+  },
+
+  /**
+   * Get/alter the currently displayed tool.
+   */
+  get currentToolId() {
+    return this._currentToolId;
+  },
+
+  set currentToolId(value) {
+    this._currentToolId = value;
+  },
+
+  /**
+   * Get the iframe containing the toolbox UI.
+   */
+  get frame() {
+    return this._host.frame;
+  },
+
+  /**
+   * Shortcut to the document containing the toolbox UI
+   */
+  get doc() {
+    return this.frame.contentDocument;
+  },
+
+  /**
+   * Open the toolbox
+   */
+  open: function TBOX_open() {
+    let deferred = Promise.defer();
+
+    this._host.create().then(function(iframe) {
+      let domReady = function() {
+        iframe.removeEventListener("DOMContentLoaded", domReady, true);
+
+        this.isReady = true;
+
+        let closeButton = this.doc.getElementById("toolbox-close");
+        closeButton.addEventListener("command", this.destroy, true);
+
+        this._buildDockButtons();
+        this._buildOptions();
+        this._buildTabs();
+        this._buildButtons();
+        this._addKeysToWindow();
+
+        this.selectTool(this._defaultToolId).then(function(panel) {
+          this.emit("ready");
+          deferred.resolve();
+        }.bind(this));
+      }.bind(this);
+
+      iframe.addEventListener("DOMContentLoaded", domReady, true);
+      iframe.setAttribute("src", this._URL);
+    }.bind(this));
+
+    return deferred.promise;
+  },
+
+  _buildOptions: function TBOX__buildOptions() {
+    this.optionsButton = this.doc.getElementById("toolbox-tab-options");
+    this.optionsButton.addEventListener("command", function() {
+      this.selectTool("options");
+    }.bind(this), false);
+
+    let iframe = this.doc.getElementById("toolbox-panel-iframe-options");
+    this._toolPanels.set("options", iframe);
+
+    let key = this.doc.getElementById("toolbox-options-key");
+    key.addEventListener("command", function(toolId) {
+      this.selectTool(toolId);
+    }.bind(this, "options"), true);
+  },
+
+  /**
+   * Adds the keys and commands to the Toolbox Window in window mode.
+   */
+  _addKeysToWindow: function TBOX__addKeysToWindow() {
+    if (this.hostType != Toolbox.HostType.WINDOW) {
+      return;
+    }
+    let doc = this.doc.defaultView.parent.document;
+    for (let [id, toolDefinition] of gDevTools.getToolDefinitionMap()) {
+      if (toolDefinition.key) {
+        // Prevent multiple entries for the same tool.
+        if (doc.getElementById("key_" + id)) {
+          continue;
+        }
+        let key = doc.createElement("key");
+        key.id = "key_" + id;
+
+        if (toolDefinition.key.startsWith("VK_")) {
+          key.setAttribute("keycode", toolDefinition.key);
+        } else {
+          key.setAttribute("key", toolDefinition.key);
+        }
+
+        key.setAttribute("modifiers", toolDefinition.modifiers);
+        key.setAttribute("oncommand", "void(0);"); // needed. See bug 371900
+        key.addEventListener("command", function(toolId) {
+          this.selectTool(toolId);
+        }.bind(this, id), true);
+        doc.getElementById("toolbox-keyset").appendChild(key);
+      }
+    }
+  },
+
+  /**
+   * Build the buttons for changing hosts. Called every time
+   * the host changes.
+   */
+  _buildDockButtons: function TBOX_createDockButtons() {
+    let dockBox = this.doc.getElementById("toolbox-dock-buttons");
+
+    while (dockBox.firstChild) {
+      dockBox.removeChild(dockBox.firstChild);
+    }
+
+    if (!this._target.isLocalTab) {
+      return;
+    }
+
+    let closeButton = this.doc.getElementById("toolbox-close");
+    if (this.hostType === this.HostType.WINDOW) {
+      closeButton.setAttribute("hidden", "true");
+    } else {
+      closeButton.removeAttribute("hidden");
+    }
+
+    let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);
+
+    for each (let position in this.HostType) {
+      if (position == this.hostType ||
+         (!sideEnabled && position == this.HostType.SIDE)) {
+        continue;
+      }
+
+      let button = this.doc.createElement("toolbarbutton");
+      button.id = "toolbox-dock-" + position;
+      button.className = "toolbox-dock-button";
+      button.setAttribute("tooltiptext", toolboxStrings("toolboxDockButtons." +
+                                                        position + ".tooltip"));
+      button.addEventListener("command", function(position) {
+        this.switchHost(position);
+      }.bind(this, position));
+
+      dockBox.appendChild(button);
+    }
+  },
+
+  /**
+   * Add tabs to the toolbox UI for registered tools
+   */
+  _buildTabs: function TBOX_buildTabs() {
+    for (let definition of gDevTools.getToolDefinitionArray()) {
+      this._buildTabForTool(definition);
+    }
+  },
+
+  /**
+   * Add buttons to the UI as specified in the devtools.window.toolbarSpec pref
+   */
+  _buildButtons: function TBOX_buildButtons() {
+    if (!this.target.isLocalTab) {
+      return;
+    }
+
+    let toolbarSpec = CommandUtils.getCommandbarSpec("devtools.toolbox.toolbarSpec");
+    let environment = { chromeDocument: this.target.tab.ownerDocument };
+    let requisition = new Requisition(environment);
+
+    let buttons = CommandUtils.createButtons(toolbarSpec, this._target, this.doc, requisition);
+
+    let container = this.doc.getElementById("toolbox-buttons");
+    buttons.forEach(function(button) {
+      container.appendChild(button);
+    }.bind(this));
+  },
+
+  /**
+   * Build a tab for one tool definition and add to the toolbox
+   *
+   * @param {string} toolDefinition
+   *        Tool definition of the tool to build a tab for.
+   */
+  _buildTabForTool: function TBOX_buildTabForTool(toolDefinition) {
+    if (!toolDefinition.isTargetSupported(this._target)) {
+      return;
+    }
+
+    let tabs = this.doc.getElementById("toolbox-tabs");
+    let deck = this.doc.getElementById("toolbox-deck");
+
+    let id = toolDefinition.id;
+
+    let radio = this.doc.createElement("radio");
+    radio.className = "toolbox-tab devtools-tab";
+    radio.id = "toolbox-tab-" + id;
+    radio.setAttribute("flex", "1");
+    radio.setAttribute("toolid", id);
+    radio.setAttribute("tooltiptext", toolDefinition.tooltip);
+
+    radio.addEventListener("command", function(id) {
+      this.selectTool(id);
+    }.bind(this, id));
+
+    if (toolDefinition.icon) {
+      let image = this.doc.createElement("image");
+      image.setAttribute("src", toolDefinition.icon);
+      radio.appendChild(image);
+    }
+
+    let label = this.doc.createElement("label");
+    label.setAttribute("value", toolDefinition.label)
+    label.setAttribute("crop", "end");
+    label.setAttribute("flex", "1");
+
+    let vbox = this.doc.createElement("vbox");
+    vbox.className = "toolbox-panel";
+    vbox.id = "toolbox-panel-" + id;
+
+    radio.appendChild(label);
+    tabs.appendChild(radio);
+    deck.appendChild(vbox);
+
+    this._addKeysToWindow();
+  },
+
+  /**
+   * Switch to the tool with the given id
+   *
+   * @param {string} id
+   *        The id of the tool to switch to
+   */
+  selectTool: function TBOX_selectTool(id) {
+    let deferred = Promise.defer();
+
+    let selected = this.doc.querySelector(".devtools-tab[selected]");
+    if (selected) {
+      selected.removeAttribute("selected");
+    }
+    let tab = this.doc.getElementById("toolbox-tab-" + id);
+    tab.setAttribute("selected", "true");
+
+    if (this._currentToolId == id) {
+      // Return the existing panel in order to have a consistent return value.
+      return Promise.resolve(this._toolPanels.get(id));
+    }
+
+    if (!this.isReady) {
+      throw new Error("Can't select tool, wait for toolbox 'ready' event");
+    }
+    let tab = this.doc.getElementById("toolbox-tab-" + id);
+
+    if (!tab) {
+      throw new Error("No tool found");
+    }
+
+    let tabstrip = this.doc.getElementById("toolbox-tabs");
+
+    // select the right tab
+    let index = -1;
+    let tabs = tabstrip.childNodes;
+    for (let i = 0; i < tabs.length; i++) {
+      if (tabs[i] === tab) {
+        index = i;
+        break;
+      }
+    }
+    tabstrip.selectedItem = tab;
+
+    // and select the right iframe
+    let deck = this.doc.getElementById("toolbox-deck");
+    // offset by 1 due to options panel
+    if (id == "options") {
+      deck.selectedIndex = 0;
+      this.optionsButton.setAttribute("checked", true);
+    }
+    else {
+      deck.selectedIndex = index != -1 ? index + 1: -1;
+      this.optionsButton.removeAttribute("checked");
+    }
+
+    let definition = gDevTools.getToolDefinitionMap().get(id);
+
+    this._currentToolId = id;
+
+    let resolveSelected = panel => {
+      this.emit("select", id);
+      this.emit(id + "-selected", panel);
+      deferred.resolve(panel);
+    };
+
+    let iframe = this.doc.getElementById("toolbox-panel-iframe-" + id);
+    if (!iframe) {
+      iframe = this.doc.createElement("iframe");
+      iframe.className = "toolbox-panel-iframe";
+      iframe.id = "toolbox-panel-iframe-" + id;
+      iframe.setAttribute("flex", 1);
+      iframe.setAttribute("forceOwnRefreshDriver", "");
+      iframe.tooltip = "aHTMLTooltip";
+
+      let vbox = this.doc.getElementById("toolbox-panel-" + id);
+      vbox.appendChild(iframe);
+
+      let boundLoad = function() {
+        iframe.removeEventListener("DOMContentLoaded", boundLoad, true);
+
+        let built = definition.build(iframe.contentWindow, this);
+        Promise.resolve(built).then(function(panel) {
+          this._toolPanels.set(id, panel);
+
+          this.emit(id + "-ready", panel);
+          gDevTools.emit(id + "-ready", this, panel);
+
+          resolveSelected(panel);
+        }.bind(this));
+      }.bind(this);
+
+      iframe.addEventListener("DOMContentLoaded", boundLoad, true);
+      iframe.setAttribute("src", definition.url);
+    } else {
+      let panel = this._toolPanels.get(id);
+      // only emit 'select' event if the iframe has been loaded
+      if (panel && (!panel.contentDocument ||
+                    panel.contentDocument.readyState == "complete")) {
+        resolveSelected(panel);
+      }
+      else if (panel) {
+        let boundLoad = function() {
+          panel.removeEventListener("DOMContentLoaded", boundLoad, true);
+          resolveSelected(panel);
+        };
+        panel.addEventListener("DOMContentLoaded", boundLoad, true);
+      }
+    }
+
+    if (id != "options") {
+      Services.prefs.setCharPref(this._prefs.LAST_TOOL, id);
+    }
+
+    return deferred.promise;
+  },
+
+  /**
+   * Raise the toolbox host.
+   */
+  raise: function TBOX_raise() {
+    this._host.raise();
+  },
+
+  /**
+   * Refresh the host's title.
+   */
+  _refreshHostTitle: function TBOX_refreshHostTitle() {
+    let toolName;
+    let toolId = this.currentToolId;
+    let toolDef = gDevTools.getToolDefinitionMap().get(toolId);
+    if (toolDef) {
+      toolName = toolDef.label;
+    } else {
+      // no tool is selected
+      toolName = toolboxStrings("toolbox.defaultTitle");
+    }
+    let title = toolboxStrings("toolbox.titleTemplate",
+                               toolName, this.target.url);
+    this._host.setTitle(title);
+  },
+
+  /**
+   * Create a host object based on the given host type.
+   *
+   * Warning: some hosts require that the toolbox target provides a reference to
+   * the attached tab. Not all Targets have a tab property - make sure you correctly
+   * mix and match hosts and targets.
+   *
+   * @param {string} hostType
+   *        The host type of the new host object
+   *
+   * @return {Host} host
+   *        The created host object
+   */
+  _createHost: function TBOX_createHost(hostType) {
+    if (!Hosts[hostType]) {
+      throw new Error('Unknown hostType: '+ hostType);
+    }
+    let newHost = new Hosts[hostType](this.target.tab);
+
+    // clean up the toolbox if its window is closed
+    newHost.on("window-closed", this.destroy);
+
+    return newHost;
+  },
+
+  /**
+   * Switch to a new host for the toolbox UI. E.g.
+   * bottom, sidebar, separate window.
+   *
+   * @param {string} hostType
+   *        The host type of the new host object
+   */
+  switchHost: function TBOX_switchHost(hostType) {
+    if (hostType == this._host.type) {
+      return;
+    }
+
+    if (!this._target.isLocalTab) {
+      return;
+    }
+
+    let newHost = this._createHost(hostType);
+    return newHost.create().then(function(iframe) {
+      // change toolbox document's parent to the new host
+      iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
+      iframe.swapFrameLoaders(this.frame);
+
+      this._host.off("window-closed", this.destroy);
+      this._host.destroy();
+
+      this._host = newHost;
+
+      Services.prefs.setCharPref(this._prefs.LAST_HOST, this._host.type);
+
+      this._buildDockButtons();
+      this._addKeysToWindow();
+
+      this.emit("host-changed");
+    }.bind(this));
+  },
+
+  /**
+   * Handler for the tool-registered event.
+   * @param  {string} event
+   *         Name of the event ("tool-registered")
+   * @param  {string} toolId
+   *         Id of the tool that was registered
+   */
+  _toolRegistered: function TBOX_toolRegistered(event, toolId) {
+    let defs = gDevTools.getToolDefinitionMap();
+    let tool = defs.get(toolId);
+
+    this._buildTabForTool(tool);
+  },
+
+  /**
+   * Handler for the tool-unregistered event.
+   * @param  {string} event
+   *         Name of the event ("tool-unregistered")
+   * @param  {string|object} toolId
+   *         Definition or id of the tool that was unregistered. Passing the
+   *         tool id should be avoided as it is a temporary measure.
+   */
+  _toolUnregistered: function TBOX_toolUnregistered(event, toolId) {
+    if (typeof toolId != "string") {
+      toolId = toolId.id;
+    }
+
+    if (this._toolPanels.has(toolId)) {
+      let instance = this._toolPanels.get(toolId);
+      instance.destroy();
+      this._toolPanels.delete(toolId);
+    }
+
+    let radio = this.doc.getElementById("toolbox-tab-" + toolId);
+    let panel = this.doc.getElementById("toolbox-panel-" + toolId);
+
+    if (radio) {
+      if (this._currentToolId == toolId) {
+        let nextToolName = null;
+        if (radio.nextSibling) {
+          nextToolName = radio.nextSibling.getAttribute("toolid");
+        }
+        if (radio.previousSibling) {
+          nextToolName = radio.previousSibling.getAttribute("toolid");
+        }
+        if (nextToolName) {
+          this.selectTool(nextToolName);
+        }
+      }
+      radio.parentNode.removeChild(radio);
+    }
+
+    if (panel) {
+      panel.parentNode.removeChild(panel);
+    }
+
+    if (this.hostType == Toolbox.HostType.WINDOW) {
+      let doc = this.doc.defaultView.parent.document;
+      let key = doc.getElementById("key_" + id);
+      if (key) {
+        key.parentNode.removeChild(key);
+      }
+    }
+  },
+
+
+  /**
+   * Get the toolbox's notification box
+   *
+   * @return The notification box element.
+   */
+  getNotificationBox: function TBOX_getNotificationBox() {
+    return this.doc.getElementById("toolbox-notificationbox");
+  },
+
+  /**
+   * Remove all UI elements, detach from target and clear up
+   */
+  destroy: function TBOX_destroy() {
+    // If several things call destroy then we give them all the same
+    // destruction promise so we're sure to destroy only once
+    if (this._destroyer) {
+      return this._destroyer;
+    }
+    // Assign the "_destroyer" property before calling the other
+    // destroyer methods to guarantee that the Toolbox's destroy
+    // method is only executed once.
+    let deferred = Promise.defer();
+    this._destroyer = deferred.promise;
+
+    this._target.off("navigate", this._refreshHostTitle);
+    this.off("select", this._refreshHostTitle);
+    this.off("host-changed", this._refreshHostTitle);
+
+    gDevTools.off("tool-registered", this._toolRegistered);
+    gDevTools.off("tool-unregistered", this._toolUnregistered);
+
+    let outstanding = [];
+
+    this._toolPanels.delete("options");
+    for (let [id, panel] of this._toolPanels) {
+      outstanding.push(panel.destroy());
+    }
+
+    let container = this.doc.getElementById("toolbox-buttons");
+    while(container.firstChild) {
+      container.removeChild(container.firstChild);
+    }
+
+    outstanding.push(this._host.destroy());
+
+    // Targets need to be notified that the toolbox is being torn down, so that
+    // remote protocol connections can be gracefully terminated.
+    if (this._target) {
+      this._target.off("close", this.destroy);
+      outstanding.push(this._target.destroy());
+    }
+    this._target = null;
+
+    Promise.all(outstanding).then(function() {
+      this.emit("destroyed");
+      // Free _host after the call to destroyed in order to let a chance
+      // to destroyed listeners to still query toolbox attributes
+      this._host = null;
+      deferred.resolve();
+    }.bind(this));
+
+    return this._destroyer;
+  }
+};
deleted file mode 100644
--- a/browser/devtools/inspector/Breadcrumbs.jsm
+++ /dev/null
@@ -1,599 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cc = Components.classes;
-const Cu = Components.utils;
-const Ci = Components.interfaces;
-
-const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
-const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
-
-this.EXPORTED_SYMBOLS = ["HTMLBreadcrumbs"];
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
-
-const LOW_PRIORITY_ELEMENTS = {
-  "HEAD": true,
-  "BASE": true,
-  "BASEFONT": true,
-  "ISINDEX": true,
-  "LINK": true,
-  "META": true,
-  "SCRIPT": true,
-  "STYLE": true,
-  "TITLE": true,
-};
-
-///////////////////////////////////////////////////////////////////////////
-//// HTML Breadcrumbs
-
-/**
- * Display the ancestors of the current node and its children.
- * Only one "branch" of children are displayed (only one line).
- *
- * FIXME: Bug 822388 - Use the BreadcrumbsWidget in the Inspector.
- *
- * Mechanism:
- * . If no nodes displayed yet:
- *    then display the ancestor of the selected node and the selected node;
- *   else select the node;
- * . If the selected node is the last node displayed, append its first (if any).
- */
-this.HTMLBreadcrumbs = function HTMLBreadcrumbs(aInspector)
-{
-  this.inspector = aInspector;
-  this.selection = this.inspector.selection;
-  this.chromeWin = this.inspector.panelWin;
-  this.chromeDoc = this.inspector.panelDoc;
-  this.DOMHelpers = new DOMHelpers(this.chromeWin);
-  this._init();
-}
-
-HTMLBreadcrumbs.prototype = {
-  _init: function BC__init()
-  {
-    this.container = this.chromeDoc.getElementById("inspector-breadcrumbs");
-    this.container.addEventListener("mousedown", this, true);
-    this.container.addEventListener("keypress", this, true);
-
-    // We will save a list of already displayed nodes in this array.
-    this.nodeHierarchy = [];
-
-    // Last selected node in nodeHierarchy.
-    this.currentIndex = -1;
-
-    // By default, hide the arrows. We let the <scrollbox> show them
-    // in case of overflow.
-    this.container.removeAttribute("overflows");
-    this.container._scrollButtonUp.collapsed = true;
-    this.container._scrollButtonDown.collapsed = true;
-
-    this.onscrollboxreflow = function() {
-      if (this.container._scrollButtonDown.collapsed)
-        this.container.removeAttribute("overflows");
-      else
-        this.container.setAttribute("overflows", true);
-    }.bind(this);
-
-    this.container.addEventListener("underflow", this.onscrollboxreflow, false);
-    this.container.addEventListener("overflow", this.onscrollboxreflow, false);
-
-    this.update = this.update.bind(this);
-    this.updateSelectors = this.updateSelectors.bind(this);
-    this.selection.on("new-node", this.update);
-    this.selection.on("pseudoclass", this.updateSelectors);
-    this.selection.on("attribute-changed", this.updateSelectors);
-    this.update();
-  },
-
-  /**
-   * Build a string that represents the node: tagName#id.class1.class2.
-   *
-   * @param aNode The node to pretty-print
-   * @returns a string
-   */
-  prettyPrintNodeAsText: function BC_prettyPrintNodeText(aNode)
-  {
-    let text = aNode.tagName.toLowerCase();
-    if (aNode.id) {
-      text += "#" + aNode.id;
-    }
-    for (let i = 0; i < aNode.classList.length; i++) {
-      text += "." + aNode.classList[i];
-    }
-    for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
-      let pseudo = PSEUDO_CLASSES[i];
-      if (DOMUtils.hasPseudoClassLock(aNode, pseudo)) {
-        text += pseudo;
-      }
-    }
-
-    return text;
-  },
-
-
-  /**
-   * Build <label>s that represent the node:
-   *   <label class="breadcrumbs-widget-item-tag">tagName</label>
-   *   <label class="breadcrumbs-widget-item-id">#id</label>
-   *   <label class="breadcrumbs-widget-item-classes">.class1.class2</label>
-   *
-   * @param aNode The node to pretty-print
-   * @returns a document fragment.
-   */
-  prettyPrintNodeAsXUL: function BC_prettyPrintNodeXUL(aNode)
-  {
-    let fragment = this.chromeDoc.createDocumentFragment();
-
-    let tagLabel = this.chromeDoc.createElement("label");
-    tagLabel.className = "breadcrumbs-widget-item-tag plain";
-
-    let idLabel = this.chromeDoc.createElement("label");
-    idLabel.className = "breadcrumbs-widget-item-id plain";
-
-    let classesLabel = this.chromeDoc.createElement("label");
-    classesLabel.className = "breadcrumbs-widget-item-classes plain";
-
-    let pseudosLabel = this.chromeDoc.createElement("label");
-    pseudosLabel.className = "breadcrumbs-widget-item-pseudo-classes plain";
-
-    tagLabel.textContent = aNode.tagName.toLowerCase();
-    idLabel.textContent = aNode.id ? ("#" + aNode.id) : "";
-
-    let classesText = "";
-    for (let i = 0; i < aNode.classList.length; i++) {
-      classesText += "." + aNode.classList[i];
-    }
-    classesLabel.textContent = classesText;
-
-    let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
-      return DOMUtils.hasPseudoClassLock(aNode, pseudo);
-    }, this);
-    pseudosLabel.textContent = pseudos.join("");
-
-    fragment.appendChild(tagLabel);
-    fragment.appendChild(idLabel);
-    fragment.appendChild(classesLabel);
-    fragment.appendChild(pseudosLabel);
-
-    return fragment;
-  },
-
-  /**
-   * Open the sibling menu.
-   *
-   * @param aButton the button representing the node.
-   * @param aNode the node we want the siblings from.
-   */
-  openSiblingMenu: function BC_openSiblingMenu(aButton, aNode)
-  {
-    // We make sure that the targeted node is selected
-    // because we want to use the nodemenu that only works
-    // for inspector.selection
-    this.selection.setNode(aNode, "breadcrumbs");
-
-    let title = this.chromeDoc.createElement("menuitem");
-    title.setAttribute("label", this.inspector.strings.GetStringFromName("breadcrumbs.siblings"));
-    title.setAttribute("disabled", "true");
-
-    let separator = this.chromeDoc.createElement("menuseparator");
-
-    let items = [title, separator];
-
-    let nodes = aNode.parentNode.childNodes;
-    for (let i = 0; i < nodes.length; i++) {
-      if (nodes[i].nodeType == aNode.ELEMENT_NODE) {
-        let item = this.chromeDoc.createElement("menuitem");
-        if (nodes[i] === aNode) {
-          item.setAttribute("disabled", "true");
-          item.setAttribute("checked", "true");
-        }
-
-        item.setAttribute("type", "radio");
-        item.setAttribute("label", this.prettyPrintNodeAsText(nodes[i]));
-
-        let selection = this.selection;
-        item.onmouseup = (function(aNode) {
-          return function() {
-            selection.setNode(aNode, "breadcrumbs");
-          }
-        })(nodes[i]);
-
-        items.push(item);
-      }
-    }
-    this.inspector.showNodeMenu(aButton, "before_start", items);
-  },
-
-  /**
-   * Generic event handler.
-   *
-   * @param nsIDOMEvent event
-   *        The DOM event object.
-   */
-  handleEvent: function BC_handleEvent(event)
-  {
-    if (event.type == "mousedown" && event.button == 0) {
-      // on Click and Hold, open the Siblings menu
-
-      let timer;
-      let container = this.container;
-
-      function openMenu(event) {
-        cancelHold();
-        let target = event.originalTarget;
-        if (target.tagName == "button") {
-          target.onBreadcrumbsHold();
-        }
-      }
-
-      function handleClick(event) {
-        cancelHold();
-        let target = event.originalTarget;
-        if (target.tagName == "button") {
-          target.onBreadcrumbsClick();
-        }
-      }
-
-      let window = this.chromeWin;
-      function cancelHold(event) {
-        window.clearTimeout(timer);
-        container.removeEventListener("mouseout", cancelHold, false);
-        container.removeEventListener("mouseup", handleClick, false);
-      }
-
-      container.addEventListener("mouseout", cancelHold, false);
-      container.addEventListener("mouseup", handleClick, false);
-      timer = window.setTimeout(openMenu, 500, event);
-    }
-
-    if (event.type == "keypress" && this.selection.isElementNode()) {
-      let node = null;
-      switch (event.keyCode) {
-        case this.chromeWin.KeyEvent.DOM_VK_LEFT:
-          if (this.currentIndex != 0) {
-            node = this.nodeHierarchy[this.currentIndex - 1].node;
-          }
-          break;
-        case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
-          if (this.currentIndex < this.nodeHierarchy.length - 1) {
-            node = this.nodeHierarchy[this.currentIndex + 1].node;
-          }
-          break;
-        case this.chromeWin.KeyEvent.DOM_VK_UP:
-          node = this.selection.node.previousSibling;
-          while (node && (node.nodeType != node.ELEMENT_NODE)) {
-            node = node.previousSibling;
-          }
-          break;
-        case this.chromeWin.KeyEvent.DOM_VK_DOWN:
-          node = this.selection.node.nextSibling;
-          while (node && (node.nodeType != node.ELEMENT_NODE)) {
-            node = node.nextSibling;
-          }
-          break;
-      }
-      if (node) {
-        this.selection.setNode(node, "breadcrumbs");
-      }
-      event.preventDefault();
-      event.stopPropagation();
-    }
-  },
-
-  /**
-   * Remove nodes and delete properties.
-   */
-  destroy: function BC_destroy()
-  {
-    this.nodeHierarchy.forEach(function(crumb) {
-      if (LayoutHelpers.isNodeConnected(crumb.node)) {
-        DOMUtils.clearPseudoClassLocks(crumb.node);
-      }
-    });
-
-    this.selection.off("new-node", this.update);
-    this.selection.off("pseudoclass", this.updateSelectors);
-    this.selection.off("attribute-changed", this.updateSelectors);
-
-    this.container.removeEventListener("underflow", this.onscrollboxreflow, false);
-    this.container.removeEventListener("overflow", this.onscrollboxreflow, false);
-    this.onscrollboxreflow = null;
-
-    this.empty();
-    this.container.removeEventListener("mousedown", this, true);
-    this.container.removeEventListener("keypress", this, true);
-    this.container = null;
-    this.nodeHierarchy = null;
-  },
-
-  /**
-   * Empty the breadcrumbs container.
-   */
-  empty: function BC_empty()
-  {
-    while (this.container.hasChildNodes()) {
-      this.container.removeChild(this.container.firstChild);
-    }
-  },
-
-  /**
-   * Re-init the cache and remove all the buttons.
-   */
-  invalidateHierarchy: function BC_invalidateHierarchy()
-  {
-    this.inspector.hideNodeMenu();
-    this.nodeHierarchy = [];
-    this.empty();
-  },
-
-  /**
-   * Set which button represent the selected node.
-   *
-   * @param aIdx Index of the displayed-button to select
-   */
-  setCursor: function BC_setCursor(aIdx)
-  {
-    // Unselect the previously selected button
-    if (this.currentIndex > -1 && this.currentIndex < this.nodeHierarchy.length) {
-      this.nodeHierarchy[this.currentIndex].button.removeAttribute("checked");
-    }
-    if (aIdx > -1) {
-      this.nodeHierarchy[aIdx].button.setAttribute("checked", "true");
-      if (this.hadFocus)
-        this.nodeHierarchy[aIdx].button.focus();
-    }
-    this.currentIndex = aIdx;
-  },
-
-  /**
-   * Get the index of the node in the cache.
-   *
-   * @param aNode
-   * @returns integer the index, -1 if not found
-   */
-  indexOf: function BC_indexOf(aNode)
-  {
-    let i = this.nodeHierarchy.length - 1;
-    for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
-      if (this.nodeHierarchy[i].node === aNode) {
-        return i;
-      }
-    }
-    return -1;
-  },
-
-  /**
-   * Remove all the buttons and their references in the cache
-   * after a given index.
-   *
-   * @param aIdx
-   */
-  cutAfter: function BC_cutAfter(aIdx)
-  {
-    while (this.nodeHierarchy.length > (aIdx + 1)) {
-      let toRemove = this.nodeHierarchy.pop();
-      this.container.removeChild(toRemove.button);
-    }
-  },
-
-  /**
-   * Build a button representing the node.
-   *
-   * @param aNode The node from the page.
-   * @returns aNode The <button>.
-   */
-  buildButton: function BC_buildButton(aNode)
-  {
-    let button = this.chromeDoc.createElement("button");
-    button.appendChild(this.prettyPrintNodeAsXUL(aNode));
-    button.className = "breadcrumbs-widget-item";
-
-    button.setAttribute("tooltiptext", this.prettyPrintNodeAsText(aNode));
-
-    button.onkeypress = function onBreadcrumbsKeypress(e) {
-      if (e.charCode == Ci.nsIDOMKeyEvent.DOM_VK_SPACE ||
-          e.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN)
-        button.click();
-    }
-
-    button.onBreadcrumbsClick = function onBreadcrumbsClick() {
-      this.selection.setNode(aNode, "breadcrumbs");
-    }.bind(this);
-
-    button.onclick = (function _onBreadcrumbsRightClick(event) {
-      button.focus();
-      if (event.button == 2) {
-        this.openSiblingMenu(button, aNode);
-      }
-    }).bind(this);
-
-    button.onBreadcrumbsHold = (function _onBreadcrumbsHold() {
-      this.openSiblingMenu(button, aNode);
-    }).bind(this);
-    return button;
-  },
-
-  /**
-   * Connecting the end of the breadcrumbs to a node.
-   *
-   * @param aNode The node to reach.
-   */
-  expand: function BC_expand(aNode)
-  {
-      let fragment = this.chromeDoc.createDocumentFragment();
-      let toAppend = aNode;
-      let lastButtonInserted = null;
-      let originalLength = this.nodeHierarchy.length;
-      let stopNode = null;
-      if (originalLength > 0) {
-        stopNode = this.nodeHierarchy[originalLength - 1].node;
-      }
-      while (toAppend && toAppend.tagName && toAppend != stopNode) {
-        let button = this.buildButton(toAppend);
-        fragment.insertBefore(button, lastButtonInserted);
-        lastButtonInserted = button;
-        this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
-        toAppend = this.DOMHelpers.getParentObject(toAppend);
-      }
-      this.container.appendChild(fragment, this.container.firstChild);
-  },
-
-  /**
-   * Get a child of a node that can be displayed in the breadcrumbs
-   * and that is probably visible. See LOW_PRIORITY_ELEMENTS.
-   *
-   * @param aNode The parent node.
-   * @returns nsIDOMNode|null
-   */
-  getInterestingFirstNode: function BC_getInterestingFirstNode(aNode)
-  {
-    let nextChild = this.DOMHelpers.getChildObject(aNode, 0);
-    let fallback = null;
-
-    while (nextChild) {
-      if (nextChild.nodeType == aNode.ELEMENT_NODE) {
-        if (!(nextChild.tagName in LOW_PRIORITY_ELEMENTS)) {
-          return nextChild;
-        }
-        if (!fallback) {
-          fallback = nextChild;
-        }
-      }
-      nextChild = this.DOMHelpers.getNextSibling(nextChild);
-    }
-    return fallback;
-  },
-
-
-  /**
-   * Find the "youngest" ancestor of a node which is already in the breadcrumbs.
-   *
-   * @param aNode
-   * @returns Index of the ancestor in the cache
-   */
-  getCommonAncestor: function BC_getCommonAncestor(aNode)
-  {
-    let node = aNode;
-    while (node) {
-      let idx = this.indexOf(node);
-      if (idx > -1) {
-        return idx;
-      } else {
-        node = this.DOMHelpers.getParentObject(node);
-      }
-    }
-    return -1;
-  },
-
-  /**
-   * Make sure that the latest node in the breadcrumbs is not the selected node
-   * if the selected node still has children.
-   */
-  ensureFirstChild: function BC_ensureFirstChild()
-  {
-    // If the last displayed node is the selected node
-    if (this.currentIndex == this.nodeHierarchy.length - 1) {
-      let node = this.nodeHierarchy[this.currentIndex].node;
-      let child = this.getInterestingFirstNode(node);
-      // If the node has a child
-      if (child) {
-        // Show this child
-        this.expand(child);
-      }
-    }
-  },
-
-  /**
-   * Ensure the selected node is visible.
-   */
-  scroll: function BC_scroll()
-  {
-    // FIXME bug 684352: make sure its immediate neighbors are visible too.
-
-    let scrollbox = this.container;
-    let element = this.nodeHierarchy[this.currentIndex].button;
-
-    // Repeated calls to ensureElementIsVisible would interfere with each other
-    // and may sometimes result in incorrect scroll positions.
-    this.chromeWin.clearTimeout(this._ensureVisibleTimeout);
-    this._ensureVisibleTimeout = this.chromeWin.setTimeout(function() {
-      scrollbox.ensureElementIsVisible(element);
-    }, ENSURE_SELECTION_VISIBLE_DELAY);
-  },
-
-  updateSelectors: function BC_updateSelectors()
-  {
-    for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
-      let crumb = this.nodeHierarchy[i];
-      let button = crumb.button;
-
-      while(button.hasChildNodes()) {
-        button.removeChild(button.firstChild);
-      }
-      button.appendChild(this.prettyPrintNodeAsXUL(crumb.node));
-      button.setAttribute("tooltiptext", this.prettyPrintNodeAsText(crumb.node));
-    }
-  },
-
-  /**
-   * Update the breadcrumbs display when a new node is selected.
-   */
-  update: function BC_update()
-  {
-    this.inspector.hideNodeMenu();
-
-    let cmdDispatcher = this.chromeDoc.commandDispatcher;
-    this.hadFocus = (cmdDispatcher.focusedElement &&
-                     cmdDispatcher.focusedElement.parentNode == this.container);
-
-    if (!this.selection.isConnected()) {
-      this.cutAfter(-1); // remove all the crumbs
-      return;
-    }
-
-    if (!this.selection.isElementNode()) {
-      this.setCursor(-1); // no selection
-      return;
-    }
-
-    let idx = this.indexOf(this.selection.node);
-
-    // Is the node already displayed in the breadcrumbs?
-    if (idx > -1) {
-      // Yes. We select it.
-      this.setCursor(idx);
-    } else {
-      // No. Is the breadcrumbs display empty?
-      if (this.nodeHierarchy.length > 0) {
-        // No. We drop all the element that are not direct ancestors
-        // of the selection
-        let parent = this.DOMHelpers.getParentObject(this.selection.node);
-        let idx = this.getCommonAncestor(parent);
-        this.cutAfter(idx);
-      }
-      // we append the missing button between the end of the breadcrumbs display
-      // and the current node.
-      this.expand(this.selection.node);
-
-      // we select the current node button
-      idx = this.indexOf(this.selection.node);
-      this.setCursor(idx);
-    }
-    // Add the first child of the very last node of the breadcrumbs if possible.
-    this.ensureFirstChild();
-    this.updateSelectors();
-
-    // Make sure the selected node and its neighbours are visible.
-    this.scroll();
-  },
-}
-
-XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
--- a/browser/devtools/inspector/CmdInspect.jsm
+++ b/browser/devtools/inspector/CmdInspect.jsm
@@ -5,18 +5,18 @@
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 this.EXPORTED_SYMBOLS = [ ];
 
 Cu.import("resource:///modules/devtools/gcli.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
 XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
                                   "resource:///modules/devtools/gDevTools.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
-                                  "resource:///modules/devtools/Target.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "devtools",
+                                  "resource:///modules/devtools/gDevTools.jsm");
 
 /**
  * 'inspect' command
  */
 gcli.addCommand({
   name: "inspect",
   description: gcli.lookup("inspectDesc"),
   manual: gcli.lookup("inspectManual"),
@@ -25,15 +25,15 @@ gcli.addCommand({
       name: "selector",
       type: "node",
       description: gcli.lookup("inspectNodeDesc"),
       manual: gcli.lookup("inspectNodeManual")
     }
   ],
   exec: function Command_inspect(args, context) {
     let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
+    let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
 
     return gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
       toolbox.getCurrentPanel().selection.setNode(args.selector, "gcli");
     }.bind(this));
   }
 });
deleted file mode 100644
--- a/browser/devtools/inspector/Highlighter.jsm
+++ /dev/null
@@ -1,799 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-
-this.EXPORTED_SYMBOLS = ["Highlighter"];
-
-const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
-  // add ":visited" and ":link" after bug 713106 is fixed
-
-/**
- * A highlighter mechanism.
- *
- * The highlighter is built dynamically into the browser element.
- * The caller is in charge of destroying the highlighter (ie, the highlighter
- * won't be destroyed if a new tab is selected for example).
- *
- * API:
- *
- *   // Constructor and destructor.
- *   Highlighter(aTab, aInspector)
- *   void destroy();
- *
- *   // Show and hide the highlighter
- *   void show();
- *   void hide();
- *   boolean isHidden();
- *
- *   // Redraw the highlighter if the visible portion of the node has changed.
- *   void invalidateSize(aScroll);
- *
- * Events:
- *
- *   "closed" - Highlighter is closing
- *   "highlighting" - Highlighter is highlighting
- *   "locked" - The selected node has been locked
- *   "unlocked" - The selected ndoe has been unlocked
- *
- * Structure:
- *  <stack class="highlighter-container">
- *    <box class="highlighter-outline-container">
- *      <box class="highlighter-outline" locked="true/false"/>
- *    </box>
- *    <box class="highlighter-controls">
- *      <box class="highlighter-nodeinfobar-container" position="top/bottom" locked="true/false">
- *        <box class="highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-top"/>
- *        <hbox class="highlighter-nodeinfobar">
- *          <toolbarbutton class="highlighter-nodeinfobar-inspectbutton highlighter-nodeinfobar-button"/>
- *          <hbox class="highlighter-nodeinfobar-text">tagname#id.class1.class2</hbox>
- *          <toolbarbutton class="highlighter-nodeinfobar-menu highlighter-nodeinfobar-button">…</toolbarbutton>
- *        </hbox>
- *        <box class="highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-bottom"/>
- *      </box>
- *    </box>
- *  </stack>
- *
- */
-
-
-/**
- * Constructor.
- *
- * @param aTarget The inspection target.
- * @param aInspector Inspector panel.
- * @param aToolbox The toolbox holding the inspector.
- */
-this.Highlighter = function Highlighter(aTarget, aInspector, aToolbox)
-{
-  this.target = aTarget;
-  this.tab = aTarget.tab;
-  this.toolbox = aToolbox;
-  this.browser = this.tab.linkedBrowser;
-  this.chromeDoc = this.tab.ownerDocument;
-  this.chromeWin = this.chromeDoc.defaultView;
-  this.inspector = aInspector
-
-  EventEmitter.decorate(this);
-
-  this._init();
-}
-
-Highlighter.prototype = {
-  get selection() {
-    return this.inspector.selection;
-  },
-
-  _init: function Highlighter__init()
-  {
-    this.unlockAndFocus = this.unlockAndFocus.bind(this);
-    this.updateInfobar = this.updateInfobar.bind(this);
-    this.highlight = this.highlight.bind(this);
-
-    let stack = this.browser.parentNode;
-    this.win = this.browser.contentWindow;
-    this._highlighting = false;
-
-    this.highlighterContainer = this.chromeDoc.createElement("stack");
-    this.highlighterContainer.className = "highlighter-container";
-
-    this.outline = this.chromeDoc.createElement("box");
-    this.outline.className = "highlighter-outline";
-
-    let outlineContainer = this.chromeDoc.createElement("box");
-    outlineContainer.appendChild(this.outline);
-    outlineContainer.className = "highlighter-outline-container";
-
-    // The controlsBox will host the different interactive
-    // elements of the highlighter (buttons, toolbars, ...).
-    let controlsBox = this.chromeDoc.createElement("box");
-    controlsBox.className = "highlighter-controls";
-    this.highlighterContainer.appendChild(outlineContainer);
-    this.highlighterContainer.appendChild(controlsBox);
-
-    // Insert the highlighter right after the browser
-    stack.insertBefore(this.highlighterContainer, stack.childNodes[1]);
-
-    this.buildInfobar(controlsBox);
-
-    this.transitionDisabler = null;
-    this.pageEventsMuter = null;
-
-    this.unlockAndFocus();
-
-    this.selection.on("new-node", this.highlight);
-    this.selection.on("new-node", this.updateInfobar);
-    this.selection.on("pseudoclass", this.updateInfobar);
-    this.selection.on("attribute-changed", this.updateInfobar);
-
-    this.onToolSelected = function(event, id) {
-      if (id != "inspector") {
-        this.chromeWin.clearTimeout(this.pageEventsMuter);
-        this.detachMouseListeners();
-        this.disabled = true;
-        this.hide();
-      } else {
-        if (!this.locked) {
-          this.attachMouseListeners();
-        }
-        this.disabled = false;
-        this.show();
-      }
-    }.bind(this);
-    this.toolbox.on("select", this.onToolSelected);
-
-    this.hidden = true;
-    this.highlight();
-  },
-
-  /**
-   * Destroy the nodes. Remove listeners.
-   */
-  destroy: function Highlighter_destroy()
-  {
-    this.inspectButton.removeEventListener("command", this.unlockAndFocus);
-    this.inspectButton = null;
-
-    this.toolbox.off("select", this.onToolSelected);
-    this.toolbox = null;
-
-    this.selection.off("new-node", this.highlight);
-    this.selection.off("new-node", this.updateInfobar);
-    this.selection.off("pseudoclass", this.updateInfobar);
-    this.selection.off("attribute-changed", this.updateInfobar);
-
-    this.detachMouseListeners();
-    this.detachPageListeners();
-
-    this.chromeWin.clearTimeout(this.transitionDisabler);
-    this.chromeWin.clearTimeout(this.pageEventsMuter);
-    this.boundCloseEventHandler = null;
-    this._contentRect = null;
-    this._highlightRect = null;
-    this._highlighting = false;
-    this.outline = null;
-    this.nodeInfo = null;
-    this.highlighterContainer.parentNode.removeChild(this.highlighterContainer);
-    this.highlighterContainer = null;
-    this.win = null
-    this.browser = null;
-    this.chromeDoc = null;
-    this.chromeWin = null;
-    this.tabbrowser = null;
-
-    this.emit("closed");
-  },
-
-  /**
-   * Show the outline, and select a node.
-   */
-  highlight: function Highlighter_highlight()
-  {
-    if (this.selection.reason != "highlighter") {
-      this.lock();
-    }
-
-    let canHighlightNode = this.selection.isNode() &&
-                          this.selection.isConnected() &&
-                          this.selection.isElementNode();
-
-    if (canHighlightNode) {
-      if (this.selection.reason != "navigateaway") {
-        this.disabled = false;
-      }
-      this.show();
-      this.updateInfobar();
-      this.invalidateSize();
-      if (!this._highlighting &&
-          this.selection.reason != "highlighter") {
-        LayoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
-      }
-    } else {
-      this.disabled = true;
-      this.hide();
-    }
-  },
-
-  /**
-   * Update the highlighter size and position.
-   */
-  invalidateSize: function Highlighter_invalidateSize()
-  {
-    let canHiglightNode = this.selection.isNode() &&
-                          this.selection.isConnected() &&
-                          this.selection.isElementNode();
-
-    if (!canHiglightNode)
-      return;
-
-    let clientRect = this.selection.node.getBoundingClientRect();
-    let rect = LayoutHelpers.getDirtyRect(this.selection.node);
-    this.highlightRectangle(rect);
-
-    this.moveInfobar();
-
-    if (this._highlighting) {
-      this.showOutline();
-      this.emit("highlighting");
-    }
-  },
-
-  /**
-   * Show the highlighter if it has been hidden.
-   */
-  show: function() {
-    if (!this.hidden || this.disabled) return;
-    this.showOutline();
-    this.showInfobar();
-    this.computeZoomFactor();
-    this.attachPageListeners();
-    this.invalidateSize();
-    this.hidden = false;
-  },
-
-  /**
-   * Hide the highlighter, the outline and the infobar.
-   */
-  hide: function() {
-    if (this.hidden) return;
-    this.hideOutline();
-    this.hideInfobar();
-    this.detachPageListeners();
-    this.hidden = true;
-  },
-
-  /**
-   * Is the highlighter visible?
-   *
-   * @return boolean
-   */
-  isHidden: function() {
-    return this.hidden;
-  },
-
-  /**
-   * Lock a node. Stops the inspection.
-   */
-  lock: function() {
-    if (this.locked === true) return;
-    this.outline.setAttribute("locked", "true");
-    this.nodeInfo.container.setAttribute("locked", "true");
-    this.detachMouseListeners();
-    this.locked = true;
-    this.emit("locked");
-  },
-
-  /**
-   * Start inspecting.
-   * Unlock the current node (if any), and select any node being hovered.
-   */
-  unlock: function() {
-    if (this.locked === false) return;
-    this.outline.removeAttribute("locked");
-    this.nodeInfo.container.removeAttribute("locked");
-    this.attachMouseListeners();
-    this.locked = false;
-    if (this.selection.isElementNode() &&
-        this.selection.isConnected()) {
-      this.showOutline();
-    }
-    this.emit("unlocked");
-  },
-
-  /**
-   * Focus the browser before unlocking.
-   */
-  unlockAndFocus: function Highlighter_unlockAndFocus() {
-    if (this.locked === false) return;
-    this.chromeWin.focus();
-    this.unlock();
-  },
-
-  /**
-   * Hide the infobar
-   */
-   hideInfobar: function Highlighter_hideInfobar() {
-     this.nodeInfo.container.setAttribute("force-transitions", "true");
-     this.nodeInfo.container.setAttribute("hidden", "true");
-   },
-
-  /**
-   * Show the infobar
-   */
-   showInfobar: function Highlighter_showInfobar() {
-     this.nodeInfo.container.removeAttribute("hidden");
-     this.moveInfobar();
-     this.nodeInfo.container.removeAttribute("force-transitions");
-   },
-
-  /**
-   * Hide the outline
-   */
-   hideOutline: function Highlighter_hideOutline() {
-     this.outline.setAttribute("hidden", "true");
-   },
-
-  /**
-   * Show the outline
-   */
-   showOutline: function Highlighter_showOutline() {
-     if (this._highlighting)
-       this.outline.removeAttribute("hidden");
-   },
-
-  /**
-   * Build the node Infobar.
-   *
-   * <box class="highlighter-nodeinfobar-container">
-   *   <box class="Highlighter-nodeinfobar-arrow-top"/>
-   *   <hbox class="highlighter-nodeinfobar">
-   *     <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"/>
-   *     <hbox class="highlighter-nodeinfobar-text">
-   *       <xhtml:span class="highlighter-nodeinfobar-tagname"/>
-   *       <xhtml:span class="highlighter-nodeinfobar-id"/>
-   *       <xhtml:span class="highlighter-nodeinfobar-classes"/>
-   *       <xhtml:span class="highlighter-nodeinfobar-pseudo-classes"/>
-   *     </hbox>
-   *     <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"/>
-   *   </hbox>
-   *   <box class="Highlighter-nodeinfobar-arrow-bottom"/>
-   * </box>
-   *
-   * @param nsIDOMElement aParent
-   *        The container of the infobar.
-   */
-  buildInfobar: function Highlighter_buildInfobar(aParent)
-  {
-    let container = this.chromeDoc.createElement("box");
-    container.className = "highlighter-nodeinfobar-container";
-    container.setAttribute("position", "top");
-    container.setAttribute("disabled", "true");
-
-    let nodeInfobar = this.chromeDoc.createElement("hbox");
-    nodeInfobar.className = "highlighter-nodeinfobar";
-
-    let arrowBoxTop = this.chromeDoc.createElement("box");
-    arrowBoxTop.className = "highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-top";
-
-    let arrowBoxBottom = this.chromeDoc.createElement("box");
-    arrowBoxBottom.className = "highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-bottom";
-
-    let tagNameLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
-    tagNameLabel.className = "highlighter-nodeinfobar-tagname";
-
-    let idLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
-    idLabel.className = "highlighter-nodeinfobar-id";
-
-    let classesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
-    classesBox.className = "highlighter-nodeinfobar-classes";
-
-    let pseudoClassesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
-    pseudoClassesBox.className = "highlighter-nodeinfobar-pseudo-classes";
-
-    // Add some content to force a better boundingClientRect down below.
-    pseudoClassesBox.textContent = "&nbsp;";
-
-    // Create buttons
-
-    this.inspectButton = this.chromeDoc.createElement("toolbarbutton");
-    this.inspectButton.className = "highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"
-    let toolbarInspectButton = this.inspector.panelDoc.getElementById("inspector-inspect-toolbutton");
-    this.inspectButton.setAttribute("tooltiptext", toolbarInspectButton.getAttribute("tooltiptext"));
-    this.inspectButton.addEventListener("command", this.unlockAndFocus);
-
-    let nodemenu = this.chromeDoc.createElement("toolbarbutton");
-    nodemenu.setAttribute("type", "menu");
-    nodemenu.className = "highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"
-    nodemenu.setAttribute("tooltiptext",
-                          this.strings.GetStringFromName("nodeMenu.tooltiptext"));
-
-    nodemenu.onclick = function() {
-      this.inspector.showNodeMenu(nodemenu, "after_start");
-    }.bind(this);
-
-    // <hbox class="highlighter-nodeinfobar-text"/>
-    let texthbox = this.chromeDoc.createElement("hbox");
-    texthbox.className = "highlighter-nodeinfobar-text";
-    texthbox.setAttribute("align", "center");
-    texthbox.setAttribute("flex", "1");
-
-    texthbox.addEventListener("mousedown", function(aEvent) {
-      // On click, show the node:
-      if (this.selection.isElementNode()) {
-        LayoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
-      }
-    }.bind(this), true);
-
-    texthbox.appendChild(tagNameLabel);
-    texthbox.appendChild(idLabel);
-    texthbox.appendChild(classesBox);
-    texthbox.appendChild(pseudoClassesBox);
-
-    nodeInfobar.appendChild(this.inspectButton);
-    nodeInfobar.appendChild(texthbox);
-    nodeInfobar.appendChild(nodemenu);
-
-    container.appendChild(arrowBoxTop);
-    container.appendChild(nodeInfobar);
-    container.appendChild(arrowBoxBottom);
-
-    aParent.appendChild(container);
-
-    let barHeight = container.getBoundingClientRect().height;
-
-    this.nodeInfo = {
-      tagNameLabel: tagNameLabel,
-      idLabel: idLabel,
-      classesBox: classesBox,
-      pseudoClassesBox: pseudoClassesBox,
-      container: container,
-      barHeight: barHeight,
-    };
-  },
-
-  /**
-   * Highlight a rectangular region.
-   *
-   * @param object aRect
-   *        The rectangle region to highlight.
-   * @returns boolean
-   *          True if the rectangle was highlighted, false otherwise.
-   */
-  highlightRectangle: function Highlighter_highlightRectangle(aRect)
-  {
-    if (!aRect) {
-      this.unhighlight();
-      return;
-    }
-
-    let oldRect = this._contentRect;
-
-    if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
-        aRect.width == oldRect.width && aRect.height == oldRect.height) {
-      return; // same rectangle
-    }
-
-    let aRectScaled = LayoutHelpers.getZoomedRect(this.win, aRect);
-
-    if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
-        aRectScaled.width > 0 && aRectScaled.height > 0) {
-
-      this.showOutline();
-
-      // The bottom div and the right div are flexibles (flex=1).
-      // We don't need to resize them.
-      let top = "top:" + aRectScaled.top + "px;";
-      let left = "left:" + aRectScaled.left + "px;";
-      let width = "width:" + aRectScaled.width + "px;";
-      let height = "height:" + aRectScaled.height + "px;";
-      this.outline.setAttribute("style", top + left + width + height);
-
-      this._highlighting = true;
-    } else {
-      this.unhighlight();
-    }
-
-    this._contentRect = aRect; // save orig (non-scaled) rect
-    this._highlightRect = aRectScaled; // and save the scaled rect.
-
-    return;
-  },
-
-  /**
-   * Clear the highlighter surface.
-   */
-  unhighlight: function Highlighter_unhighlight()
-  {
-    this._highlighting = false;
-    this.hideOutline();
-  },
-
-  /**
-   * Update node information (tagName#id.class)
-   */
-  updateInfobar: function Highlighter_updateInfobar()
-  {
-    if (!this.selection.isElementNode()) {
-      this.nodeInfo.tagNameLabel.textContent = "";
-      this.nodeInfo.idLabel.textContent = "";
-      this.nodeInfo.classesBox.textContent = "";
-      this.nodeInfo.pseudoClassesBox.textContent = "";
-      return;
-    }
-
-    let node = this.selection.node;
-
-    // Tag name
-    this.nodeInfo.tagNameLabel.textContent = node.tagName;
-
-    // ID
-    this.nodeInfo.idLabel.textContent = node.id ? "#" + node.id : "";
-
-    // Classes
-    let classes = this.nodeInfo.classesBox;
-
-    classes.textContent = node.classList.length ?
-                            "." + Array.join(node.classList, ".") : "";
-
-    // Pseudo-classes
-    let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
-      return DOMUtils.hasPseudoClassLock(node, pseudo);
-    }, this);
-
-    let pseudoBox = this.nodeInfo.pseudoClassesBox;
-    pseudoBox.textContent = pseudos.join("");
-  },
-
-  /**
-   * Move the Infobar to the right place in the highlighter.
-   */
-  moveInfobar: function Highlighter_moveInfobar()
-  {
-    if (this._highlightRect) {
-      let winHeight = this.win.innerHeight * this.zoom;
-      let winWidth = this.win.innerWidth * this.zoom;
-
-      let rect = {top: this._highlightRect.top,
-                  left: this._highlightRect.left,
-                  width: this._highlightRect.width,
-                  height: this._highlightRect.height};
-
-      rect.top = Math.max(rect.top, 0);
-      rect.left = Math.max(rect.left, 0);
-      rect.width = Math.max(rect.width, 0);
-      rect.height = Math.max(rect.height, 0);
-
-      rect.top = Math.min(rect.top, winHeight);
-      rect.left = Math.min(rect.left, winWidth);
-
-      this.nodeInfo.container.removeAttribute("disabled");
-      // Can the bar be above the node?
-      if (rect.top < this.nodeInfo.barHeight) {
-        // No. Can we move the toolbar under the node?
-        if (rect.top + rect.height +
-            this.nodeInfo.barHeight > winHeight) {
-          // No. Let's move it inside.
-          this.nodeInfo.container.style.top = rect.top + "px";
-          this.nodeInfo.container.setAttribute("position", "overlap");
-        } else {
-          // Yes. Let's move it under the node.
-          this.nodeInfo.container.style.top = rect.top + rect.height + "px";
-          this.nodeInfo.container.setAttribute("position", "bottom");
-        }
-      } else {
-        // Yes. Let's move it on top of the node.
-        this.nodeInfo.container.style.top =
-          rect.top - this.nodeInfo.barHeight + "px";
-        this.nodeInfo.container.setAttribute("position", "top");
-      }
-
-      let barWidth = this.nodeInfo.container.getBoundingClientRect().width;
-      let left = rect.left + rect.width / 2 - barWidth / 2;
-
-      // Make sure the whole infobar is visible
-      if (left < 0) {
-        left = 0;
-        this.nodeInfo.container.setAttribute("hide-arrow", "true");
-      } else {
-        if (left + barWidth > winWidth) {
-          left = winWidth - barWidth;
-          this.nodeInfo.container.setAttribute("hide-arrow", "true");
-        } else {
-          this.nodeInfo.container.removeAttribute("hide-arrow");
-        }
-      }
-      this.nodeInfo.container.style.left = left + "px";
-    } else {
-      this.nodeInfo.container.style.left = "0";
-      this.nodeInfo.container.style.top = "0";
-      this.nodeInfo.container.setAttribute("position", "top");
-      this.nodeInfo.container.setAttribute("hide-arrow", "true");
-    }
-  },
-
-  /**
-   * Store page zoom factor.
-   */
-  computeZoomFactor: function Highlighter_computeZoomFactor() {
-    this.zoom =
-      this.win.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIDOMWindowUtils)
-      .fullZoom;
-  },
-
-  /////////////////////////////////////////////////////////////////////////
-  //// Event Handling
-
-  attachMouseListeners: function Highlighter_attachMouseListeners()
-  {
-    this.browser.addEventListener("mousemove", this, true);
-    this.browser.addEventListener("click", this, true);
-    this.browser.addEventListener("dblclick", this, true);
-    this.browser.addEventListener("mousedown", this, true);
-    this.browser.addEventListener("mouseup", this, true);
-  },
-
-  detachMouseListeners: function Highlighter_detachMouseListeners()
-  {
-    this.browser.removeEventListener("mousemove", this, true);
-    this.browser.removeEventListener("click", this, true);
-    this.browser.removeEventListener("dblclick", this, true);
-    this.browser.removeEventListener("mousedown", this, true);
-    this.browser.removeEventListener("mouseup", this, true);
-  },
-
-  attachPageListeners: function Highlighter_attachPageListeners()
-  {
-    this.browser.addEventListener("resize", this, true);
-    this.browser.addEventListener("scroll", this, true);
-    this.browser.addEventListener("MozAfterPaint", this, true);
-  },
-
-  detachPageListeners: function Highlighter_detachPageListeners()
-  {
-    this.browser.removeEventListener("resize", this, true);
-    this.browser.removeEventListener("scroll", this, true);
-    this.browser.removeEventListener("MozAfterPaint", this, true);
-  },
-
-  /**
-   * Generic event handler.
-   *
-   * @param nsIDOMEvent aEvent
-   *        The DOM event object.
-   */
-  handleEvent: function Highlighter_handleEvent(aEvent)
-  {
-    switch (aEvent.type) {
-      case "click":
-        this.handleClick(aEvent);
-        break;
-      case "mousemove":
-        this.brieflyIgnorePageEvents();
-        this.handleMouseMove(aEvent);
-        break;
-      case "resize":
-        this.computeZoomFactor();
-        break;
-      case "MozAfterPaint":
-      case "scroll":
-        this.brieflyDisableTransitions();
-        this.invalidateSize();
-        break;
-      case "dblclick":
-      case "mousedown":
-      case "mouseup":
-        aEvent.stopPropagation();
-        aEvent.preventDefault();
-        break;
-    }
-  },
-
-  /**
-   * Disable the CSS transitions for a short time to avoid laggy animations
-   * during scrolling or resizing.
-   */
-  brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
-  {
-    if (this.transitionDisabler) {
-      this.chromeWin.clearTimeout(this.transitionDisabler);
-    } else {
-      this.outline.setAttribute("disable-transitions", "true");
-      this.nodeInfo.container.setAttribute("disable-transitions", "true");
-    }
-    this.transitionDisabler =
-      this.chromeWin.setTimeout(function() {
-        this.outline.removeAttribute("disable-transitions");
-        this.nodeInfo.container.removeAttribute("disable-transitions");
-        this.transitionDisabler = null;
-      }.bind(this), 500);
-  },
-
-  /**
-   * Don't listen to page events while inspecting with the mouse.
-   */
-  brieflyIgnorePageEvents: function Highlighter_brieflyIgnorePageEvents()
-  {
-    // The goal is to keep smooth animations while inspecting.
-    // CSS Transitions might be interrupted because of a MozAfterPaint
-    // event that would triger an invalidateSize() call.
-    // So we don't listen to events that would trigger an invalidateSize()
-    // call.
-    //
-    // Side effect, zoom levels are not updated during this short period.
-    // It's very unlikely this would happen, but just in case, we call
-    // computeZoomFactor() when reattaching the events.
-    if (this.pageEventsMuter) {
-      this.chromeWin.clearTimeout(this.pageEventsMuter);
-    } else {
-      this.detachPageListeners();
-    }
-    this.pageEventsMuter =
-      this.chromeWin.setTimeout(function() {
-        this.attachPageListeners();
-        // Just in case the zoom level changed while ignoring the paint events
-        this.computeZoomFactor();
-        this.pageEventsMuter = null;
-      }.bind(this), 500);
-  },
-
-  /**
-   * Handle clicks.
-   *
-   * @param nsIDOMEvent aEvent
-   *        The DOM event.
-   */
-  handleClick: function Highlighter_handleClick(aEvent)
-  {
-    // Stop inspection when the user clicks on a node.
-    if (aEvent.button == 0) {
-      this.lock();
-      let node = this.selection.node;
-      this.selection.setNode(node, "highlighter-lock");
-      aEvent.preventDefault();
-      aEvent.stopPropagation();
-    }
-  },
-
-  /**
-   * Handle mousemoves in panel.
-   *
-   * @param nsiDOMEvent aEvent
-   *        The MouseEvent triggering the method.
-   */
-  handleMouseMove: function Highlighter_handleMouseMove(aEvent)
-  {
-    let doc = aEvent.target.ownerDocument;
-
-    // This should never happen, but just in case, we don't let the
-    // highlighter highlight browser nodes.
-    if (doc && doc != this.chromeDoc) {
-      let element = LayoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
-        aEvent.clientX, aEvent.clientY);
-      if (element && element != this.selection.node) {
-        this.selection.setNode(element, "highlighter");
-      }
-    }
-  },
-};
-
-///////////////////////////////////////////////////////////////////////////
-
-XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
-});
-
-XPCOMUtils.defineLazyGetter(Highlighter.prototype, "strings", function () {
-    return Services.strings.createBundle(
-            "chrome://browser/locale/devtools/inspector.properties");
-});
deleted file mode 100644
--- a/browser/devtools/inspector/InspectorPanel.jsm
+++ /dev/null
@@ -1,640 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-this.EXPORTED_SYMBOLS = ["InspectorPanel"];
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-Cu.import("resource:///modules/devtools/CssLogic.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "MarkupView",
-  "resource:///modules/devtools/MarkupView.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Selection",
-  "resource:///modules/devtools/Selection.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "HTMLBreadcrumbs",
-  "resource:///modules/devtools/Breadcrumbs.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Highlighter",
-  "resource:///modules/devtools/Highlighter.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ToolSidebar",
-  "resource:///modules/devtools/Sidebar.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "SelectorSearch",
-  "resource:///modules/devtools/SelectorSearch.jsm");
-
-const LAYOUT_CHANGE_TIMER = 250;
-
-/**
- * Represents an open instance of the Inspector for a tab.
- * The inspector controls the highlighter, the breadcrumbs,
- * the markup view, and the sidebar (computed view, rule view
- * and layout view).
- */
-this.InspectorPanel = function InspectorPanel(iframeWindow, toolbox) {
-  this._toolbox = toolbox;
-  this._target = toolbox._target;
-  this.panelDoc = iframeWindow.document;
-  this.panelWin = iframeWindow;
-  this.panelWin.inspector = this;
-
-  EventEmitter.decorate(this);
-}
-
-InspectorPanel.prototype = {
-  /**
-   * open is effectively an asynchronous constructor
-   */
-  open: function InspectorPanel_open() {
-    let deferred = Promise.defer();
-
-    this.onNavigatedAway = this.onNavigatedAway.bind(this);
-    this.target.on("navigate", this.onNavigatedAway);
-
-    this.nodemenu = this.panelDoc.getElementById("inspector-node-popup");
-    this.lastNodemenuItem = this.nodemenu.lastChild;
-    this._setupNodeMenu = this._setupNodeMenu.bind(this);
-    this._resetNodeMenu = this._resetNodeMenu.bind(this);
-    this.nodemenu.addEventListener("popupshowing", this._setupNodeMenu, true);
-    this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
-
-    // Create an empty selection
-    this._selection = new Selection();
-    this.onNewSelection = this.onNewSelection.bind(this);
-    this.selection.on("new-node", this.onNewSelection);
-    this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
-    this.selection.on("before-new-node", this.onBeforeNewSelection);
-    this.onDetached = this.onDetached.bind(this);
-    this.selection.on("detached", this.onDetached);
-
-    this.breadcrumbs = new HTMLBreadcrumbs(this);
-
-    if (this.target.isLocalTab) {
-      this.browser = this.target.tab.linkedBrowser;
-      this.scheduleLayoutChange = this.scheduleLayoutChange.bind(this);
-      this.browser.addEventListener("resize", this.scheduleLayoutChange, true);
-
-      this.highlighter = new Highlighter(this.target, this, this._toolbox);
-      let button = this.panelDoc.getElementById("inspector-inspect-toolbutton");
-      button.hidden = false;
-      this.onLockStateChanged = function() {
-        if (this.highlighter.locked) {
-          button.removeAttribute("checked");
-          this._toolbox.raise();
-        } else {
-          button.setAttribute("checked", "true");
-        }
-      }.bind(this);
-      this.highlighter.on("locked", this.onLockStateChanged);
-      this.highlighter.on("unlocked", this.onLockStateChanged);
-
-      // Show a warning when the debugger is paused.
-      // We show the warning only when the inspector
-      // is selected.
-      this.updateDebuggerPausedWarning = function() {
-        let notificationBox = this._toolbox.getNotificationBox();
-        let notification = notificationBox.getNotificationWithValue("inspector-script-paused");
-        if (!notification && this._toolbox.currentToolId == "inspector" &&
-            this.target.isThreadPaused) {
-          let message = this.strings.GetStringFromName("debuggerPausedWarning.message");
-          notificationBox.appendNotification(message,
-            "inspector-script-paused", "", notificationBox.PRIORITY_WARNING_HIGH);
-        }
-
-        if (notification && this._toolbox.currentToolId != "inspector") {
-          notificationBox.removeNotification(notification);
-        }
-
-        if (notification && !this.target.isThreadPaused) {
-          notificationBox.removeNotification(notification);
-        }
-
-      }.bind(this);
-      this.target.on("thread-paused", this.updateDebuggerPausedWarning);
-      this.target.on("thread-resumed", this.updateDebuggerPausedWarning);
-      this._toolbox.on("select", this.updateDebuggerPausedWarning);
-      this.updateDebuggerPausedWarning();
-    }
-
-    this._initMarkup();
-    this.isReady = false;
-
-    this.once("markuploaded", function() {
-      this.isReady = true;
-
-      // All the components are initialized. Let's select a node.
-      if (this.target.isLocalTab) {
-        let root = this.browser.contentDocument.documentElement;
-        this._selection.setNode(root);
-      } else if (this.target.window) {
-        let root = this.target.window.document.documentElement;
-        this._selection.setNode(root);
-      }
-
-      if (this.highlighter) {
-        this.highlighter.unlock();
-      }
-
-      this.emit("ready");
-      deferred.resolve(this);
-    }.bind(this));
-
-    this.setupSearchBox();
-    this.setupSidebar();
-
-    return deferred.promise;
-  },
-
-  /**
-   * Selection object (read only)
-   */
-  get selection() {
-    return this._selection;
-  },
-
-  /**
-   * Target getter.
-   */
-  get target() {
-    return this._target;
-  },
-
-  /**
-   * Target setter.
-   */
-  set target(value) {
-    this._target = value;
-  },
-
-  /**
-   * Expose gViewSourceUtils so that other tools can make use of them.
-   */
-  get viewSourceUtils() {
-    return this.panelWin.gViewSourceUtils;
-  },
-
-  /**
-   * Indicate that a tool has modified the state of the page.  Used to
-   * decide whether to show the "are you sure you want to navigate"
-   * notification.
-   */
-  markDirty: function InspectorPanel_markDirty() {
-    this.isDirty = true;
-  },
-
-  /**
-   * Hooks the searchbar to show result and auto completion suggestions.
-   */
-  setupSearchBox: function InspectorPanel_setupSearchBox() {
-    // Initiate the selectors search object.
-    let setNodeFunction = function(node) {
-      this.selection.setNode(node, "selectorsearch");
-    }.bind(this);
-    if (this.searchSuggestions) {
-      this.searchSuggestions.destroy();
-      this.searchSuggestions = null;
-    }
-    this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
-    this.searchSuggestions = new SelectorSearch(this.browser.contentDocument,
-                                                this.searchBox,
-                                                setNodeFunction);
-  },
-
-  /**
-   * Build the sidebar.
-   */
-  setupSidebar: function InspectorPanel_setupSidebar() {
-    let tabbox = this.panelDoc.querySelector("#inspector-sidebar");
-    this.sidebar = new ToolSidebar(tabbox, this);
-
-    let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
-
-    this._setDefaultSidebar = function(event, toolId) {
-      Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);
-    }.bind(this);
-
-    this.sidebar.on("select", this._setDefaultSidebar);
-    this.toggleHighlighter = this.toggleHighlighter.bind(this);
-
-    this.sidebar.addTab("ruleview",
-                        "chrome://browser/content/devtools/cssruleview.xhtml",
-                        "ruleview" == defaultTab);
-
-    this.sidebar.addTab("computedview",
-                        "chrome://browser/content/devtools/computedview.xhtml",
-                        "computedview" == defaultTab);
-
-    if (Services.prefs.getBoolPref("devtools.fontinspector.enabled")) {
-      this.sidebar.addTab("fontinspector",
-                          "chrome://browser/content/devtools/fontinspector/font-inspector.xhtml",
-                          "fontinspector" == defaultTab);
-    }
-
-    this.sidebar.addTab("layoutview",
-                        "chrome://browser/content/devtools/layoutview/view.xhtml",
-                        "layoutview" == defaultTab);
-
-    let ruleViewTab = this.sidebar.getTab("ruleview");
-    ruleViewTab.addEventListener("mouseover", this.toggleHighlighter, false);
-    ruleViewTab.addEventListener("mouseout", this.toggleHighlighter, false);
-
-    this.sidebar.show();
-  },
-
-  /**
-   * Reset the inspector on navigate away.
-   */
-  onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
-    let newWindow = payload._navPayload || payload;
-    this.selection.setNode(null);
-    this._destroyMarkup();
-    this.isDirty = false;
-    let self = this;
-
-    function onDOMReady() {
-      newWindow.removeEventListener("DOMContentLoaded", onDOMReady, true);
-
-      if (self._destroyed) {
-        return;
-      }
-
-      if (!self.selection.node) {
-        self.selection.setNode(newWindow.document.documentElement, "navigateaway");
-      }
-      self._initMarkup();
-      self.setupSearchBox();
-    }
-
-    if (newWindow.document.readyState == "loading") {
-      newWindow.addEventListener("DOMContentLoaded", onDOMReady, true);
-    } else {
-      onDOMReady();
-    }
-  },
-
-  /**
-   * When a new node is selected.
-   */
-  onNewSelection: function InspectorPanel_onNewSelection() {
-    this.cancelLayoutChange();
-  },
-
-  /**
-   * When a new node is selected, before the selection has changed.
-   */
-  onBeforeNewSelection: function InspectorPanel_onBeforeNewSelection(event,
-                                                                     node) {
-    if (this.breadcrumbs.indexOf(node) == -1) {
-      // only clear locks if we'd have to update breadcrumbs
-      this.clearPseudoClasses();
-    }
-  },
-
-  /**
-   * When a node is deleted, select its parent node.
-   */
-  onDetached: function InspectorPanel_onDetached(event, parentNode) {
-    this.cancelLayoutChange();
-    this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
-    this.selection.setNode(parentNode, "detached");
-  },
-
-  /**
-   * Destroy the inspector.
-   */
-  destroy: function InspectorPanel__destroy() {
-    if (this._destroyed) {
-      return Promise.resolve(null);
-    }
-    this._destroyed = true;
-
-    this.cancelLayoutChange();
-
-    if (this.browser) {
-      this.browser.removeEventListener("resize", this.scheduleLayoutChange, true);
-      this.browser = null;
-    }
-
-    this.target.off("navigate", this.onNavigatedAway);
-
-    if (this.highlighter) {
-      this.highlighter.off("locked", this.onLockStateChanged);
-      this.highlighter.off("unlocked", this.onLockStateChanged);
-      this.highlighter.destroy();
-    }
-
-    this.target.off("thread-paused", this.updateDebuggerPausedWarning);
-    this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
-    this._toolbox.off("select", this.updateDebuggerPausedWarning);
-
-    this._toolbox = null;
-
-    this.sidebar.off("select", this._setDefaultSidebar);
-    this.sidebar.destroy();
-    this.sidebar = null;
-
-    this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
-    this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
-    this.breadcrumbs.destroy();
-    this.searchSuggestions.destroy();
-    this.selection.off("new-node", this.onNewSelection);
-    this.selection.off("before-new-node", this.onBeforeNewSelection);
-    this.selection.off("detached", this.onDetached);
-    this._destroyMarkup();
-    this._selection.destroy();
-    this._selection = null;
-    this.panelWin.inspector = null;
-    this.target = null;
-    this.panelDoc = null;
-    this.panelWin = null;
-    this.breadcrumbs = null;
-    this.searchSuggestions = null;
-    this.lastNodemenuItem = null;
-    this.nodemenu = null;
-    this.highlighter = null;
-
-    return Promise.resolve(null);
-  },
-
-  /**
-   * Show the node menu.
-   */
-  showNodeMenu: function InspectorPanel_showNodeMenu(aButton, aPosition, aExtraItems) {
-    if (aExtraItems) {
-      for (let item of aExtraItems) {
-        this.nodemenu.appendChild(item);
-      }
-    }
-    this.nodemenu.openPopup(aButton, aPosition, 0, 0, true, false);
-  },
-
-  hideNodeMenu: function InspectorPanel_hideNodeMenu() {
-    this.nodemenu.hidePopup();
-  },
-
-  /**
-   * Disable the delete item if needed. Update the pseudo classes.
-   */
-  _setupNodeMenu: function InspectorPanel_setupNodeMenu() {
-    // Set the pseudo classes
-    for (let name of ["hover", "active", "focus"]) {
-      let menu = this.panelDoc.getElementById("node-menu-pseudo-" + name);
-
-      if (this.selection.isElementNode()) {
-        let checked = DOMUtils.hasPseudoClassLock(this.selection.node, ":" + name);
-        menu.setAttribute("checked", checked);
-        menu.removeAttribute("disabled");
-      } else {
-        menu.setAttribute("disabled", "true");
-      }
-    }
-
-    // Disable delete item if needed
-    let deleteNode = this.panelDoc.getElementById("node-menu-delete");
-    if (this.selection.isRoot() || this.selection.isDocumentTypeNode()) {
-      deleteNode.setAttribute("disabled", "true");
-    } else {
-      deleteNode.removeAttribute("disabled");
-    }
-
-    // Disable / enable "Copy Unique Selector", "Copy inner HTML" &
-    // "Copy outer HTML" as appropriate
-    let unique = this.panelDoc.getElementById("node-menu-copyuniqueselector");
-    let copyInnerHTML = this.panelDoc.getElementById("node-menu-copyinner");
-    let copyOuterHTML = this.panelDoc.getElementById("node-menu-copyouter");
-    if (this.selection.isElementNode()) {
-      unique.removeAttribute("disabled");
-      copyInnerHTML.removeAttribute("disabled");
-      copyOuterHTML.removeAttribute("disabled");
-    } else {
-      unique.setAttribute("disabled", "true");
-      copyInnerHTML.setAttribute("disabled", "true");
-      copyOuterHTML.setAttribute("disabled", "true");
-    }
-  },
-
-  _resetNodeMenu: function InspectorPanel_resetNodeMenu() {
-    // Remove any extra items
-    while (this.lastNodemenuItem.nextSibling) {
-      let toDelete = this.lastNodemenuItem.nextSibling;
-      toDelete.parentNode.removeChild(toDelete);
-    }
-  },
-
-  _initMarkup: function InspectorPanel_initMarkup() {
-    let doc = this.panelDoc;
-
-    this._markupBox = doc.getElementById("markup-box");
-
-    // create tool iframe
-    this._markupFrame = doc.createElement("iframe");
-    this._markupFrame.setAttribute("flex", "1");
-    this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
-    this._markupFrame.setAttribute("context", "inspector-node-popup");
-
-    // This is needed to enable tooltips inside the iframe document.
-    this._boundMarkupFrameLoad = function InspectorPanel_initMarkupPanel_onload() {
-      this._markupFrame.contentWindow.focus();
-      this._onMarkupFrameLoad();
-    }.bind(this);
-    this._markupFrame.addEventListener("load", this._boundMarkupFrameLoad, true);
-
-    this._markupBox.setAttribute("hidden", true);
-    this._markupBox.appendChild(this._markupFrame);
-    this._markupFrame.setAttribute("src", "chrome://browser/content/devtools/markup-view.xhtml");
-  },
-
-  _onMarkupFrameLoad: function InspectorPanel__onMarkupFrameLoad() {
-    this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
-    delete this._boundMarkupFrameLoad;
-
-    this._markupBox.removeAttribute("hidden");
-
-    let controllerWindow = this._toolbox.doc.defaultView;
-    this.markup = new MarkupView(this, this._markupFrame, controllerWindow);
-
-    this.emit("markuploaded");
-  },
-
-  _destroyMarkup: function InspectorPanel__destroyMarkup() {
-    if (this._boundMarkupFrameLoad) {
-      this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
-      delete this._boundMarkupFrameLoad;
-    }
-
-    if (this.markup) {
-      this.markup.destroy();
-      delete this.markup;
-    }
-
-    if (this._markupFrame) {
-      this._markupFrame.parentNode.removeChild(this._markupFrame);
-      delete this._markupFrame;
-    }
-  },
-
-  /**
-   * Toggle a pseudo class.
-   */
-  togglePseudoClass: function InspectorPanel_togglePseudoClass(aPseudo) {
-    if (this.selection.isElementNode()) {
-      if (DOMUtils.hasPseudoClassLock(this.selection.node, aPseudo)) {
-        this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
-          DOMUtils.removePseudoClassLock(crumb.node, aPseudo);
-        });
-      } else {
-        let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
-        let node = this.selection.node;
-        do {
-          DOMUtils.addPseudoClassLock(node, aPseudo);
-          node = node.parentNode;
-        } while (hierarchical && node.parentNode)
-      }
-    }
-    this.selection.emit("pseudoclass");
-    this.breadcrumbs.scroll();
-  },
-
-  /**
-   * Clear any pseudo-class locks applied to the current hierarchy.
-   */
-  clearPseudoClasses: function InspectorPanel_clearPseudoClasses() {
-    this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
-      try {
-        DOMUtils.clearPseudoClassLocks(crumb.node);
-      } catch(e) {
-       // Ignore dead nodes after navigation.
-      }
-    });
-  },
-
-  /**
-   * Toggle the highlighter when ruleview is hovered.
-   */
-  toggleHighlighter: function InspectorPanel_toggleHighlighter(event)
-  {
-    if (event.type == "mouseover") {
-      this.highlighter.hide();
-    }
-    else if (event.type == "mouseout") {
-      this.highlighter.show();
-    }
-  },
-
-  /**
-   * Copy the innerHTML of the selected Node to the clipboard.
-   */
-  copyInnerHTML: function InspectorPanel_copyInnerHTML()
-  {
-    if (!this.selection.isNode()) {
-      return;
-    }
-    let toCopy = this.selection.node.innerHTML;
-    if (toCopy) {
-      clipboardHelper.copyString(toCopy);
-    }
-  },
-
-  /**
-   * Copy the outerHTML of the selected Node to the clipboard.
-   */
-  copyOuterHTML: function InspectorPanel_copyOuterHTML()
-  {
-    if (!this.selection.isNode()) {
-      return;
-    }
-    let toCopy = this.selection.node.outerHTML;
-    if (toCopy) {
-      clipboardHelper.copyString(toCopy);
-    }
-  },
-
-  /**
-   * Copy a unique selector of the selected Node to the clipboard.
-   */
-  copyUniqueSelector: function InspectorPanel_copyUniqueSelector()
-  {
-    if (!this.selection.isNode()) {
-      return;
-    }
-
-    let toCopy = CssLogic.findCssSelector(this.selection.node);
-    if (toCopy) {
-      clipboardHelper.copyString(toCopy);
-    }
-  },
-
-  /**
-   * Delete the selected node.
-   */
-  deleteNode: function IUI_deleteNode() {
-    if (!this.selection.isNode() ||
-         this.selection.isRoot()) {
-      return;
-    }
-
-    let toDelete = this.selection.node;
-
-    let parent = this.selection.node.parentNode;
-
-    // If the markup panel is active, use the markup panel to delete
-    // the node, making this an undoable action.
-    if (this.markup) {
-      this.markup.deleteNode(toDelete);
-    } else {
-      // remove the node from content
-      parent.removeChild(toDelete);
-    }
-  },
-
-  /**
-   * Schedule a low-priority change event for things like paint
-   * and resize.
-   */
-  scheduleLayoutChange: function Inspector_scheduleLayoutChange()
-  {
-    if (this._timer) {
-      return null;
-    }
-    this._timer = this.panelWin.setTimeout(function() {
-      this.emit("layout-change");
-      this._timer = null;
-    }.bind(this), LAYOUT_CHANGE_TIMER);
-  },
-
-  /**
-   * Cancel a pending low-priority change event if any is
-   * scheduled.
-   */
-  cancelLayoutChange: function Inspector_cancelLayoutChange()
-  {
-    if (this._timer) {
-      this.panelWin.clearTimeout(this._timer);
-      delete this._timer;
-    }
-  },
-
-}
-
-/////////////////////////////////////////////////////////////////////////
-//// Initializers
-
-XPCOMUtils.defineLazyGetter(InspectorPanel.prototype, "strings",
-  function () {
-    return Services.strings.createBundle(
-            "chrome://browser/locale/devtools/inspector.properties");
-  });
-
-XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
-  return Cc["@mozilla.org/widget/clipboardhelper;1"].
-    getService(Ci.nsIClipboardHelper);
-});
-
-
-XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
--- a/browser/devtools/inspector/Makefile.in
+++ b/browser/devtools/inspector/Makefile.in
@@ -8,8 +8,9 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools/
+	$(NSINSTALL) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools/inspector
deleted file mode 100644
--- a/browser/devtools/inspector/Selection.jsm
+++ /dev/null
@@ -1,235 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cu = Components.utils;
-Cu.import("resource:///modules/devtools/EventEmitter.jsm");
-
-this.EXPORTED_SYMBOLS = ["Selection"];
-
-/**
- * API
- *
- *   new Selection(node=null, track={attributes,detached});
- *   destroy()
- *   node (readonly)
- *   setNode(node, origin="unknown")
- *
- * Helpers:
- *
- *   window
- *   document
- *   isRoot()
- *   isNode()
- *   isHTMLNode()
- *
- * Check the nature of the node:
- *
- *   isElementNode()
- *   isAttributeNode()
- *   isTextNode()
- *   isCDATANode()
- *   isEntityRefNode()
- *   isEntityNode()
- *   isProcessingInstructionNode()
- *   isCommentNode()
- *   isDocumentNode()
- *   isDocumentTypeNode()
- *   isDocumentFragmentNode()
- *   isNotationNode()
- *
- * Events:
- *   "new-node" when the inner node changed
- *   "before-new-node" when the inner node is set to change
- *   "attribute-changed" when an attribute is changed (only if tracked)
- *   "detached" when the node (or one of its parents) is removed from the document (only if tracked)
- *   "reparented" when the node (or one of its parents) is moved under a different node (only if tracked)
- */
-
-/**
- * A Selection object. Hold a reference to a node.
- * Includes some helpers, fire some helpful events.
- *
- * @param node Inner node.
- *    Can be null. Can be (un)set in the future via the "node" property;
- * @param trackAttribute Tell if events should be fired when the attributes of
- *    the ndoe change.
- *
- */
-this.Selection = function Selection(node=null, track={attributes:true,detached:true}) {
-  EventEmitter.decorate(this);
-  this._onMutations = this._onMutations.bind(this);
-  this.track = track;
-  this.setNode(node);
-}
-
-Selection.prototype = {
-  _node: null,
-
-  _onMutations: function(mutations) {
-    let attributeChange = false;
-    let detached = false;
-    let parentNode = null;
-    for (let m of mutations) {
-      if (!attributeChange && m.type == "attributes") {
-        attributeChange = true;
-      }
-      if (m.type == "childList") {
-        if (!detached && !this.isConnected()) {
-          parentNode = m.target;
-          detached = true;
-        }
-      }
-    }
-
-    if (attributeChange)
-      this.emit("attribute-changed");
-    if (detached)
-      this.emit("detached", parentNode);
-  },
-
-  _attachEvents: function SN__attachEvents() {
-    if (!this.window || !this.isNode() || !this.track) {
-      return;
-    }
-
-    if (this.track.attributes) {
-      this._nodeObserver = new this.window.MutationObserver(this._onMutations);
-      this._nodeObserver.observe(this.node, {attributes: true});
-    }
-
-    if (this.track.detached) {
-      this._docObserver = new this.window.MutationObserver(this._onMutations);
-      this._docObserver.observe(this.document.documentElement, {childList: true, subtree: true});
-    }
-  },
-
-  _detachEvents: function SN__detachEvents() {
-    // `disconnect` fail if node's document has
-    // been deleted.
-    try {
-      if (this._nodeObserver)
-        this._nodeObserver.disconnect();
-    } catch(e) {}
-    try {
-      if (this._docObserver)
-        this._docObserver.disconnect();
-    } catch(e) {}
-  },
-
-  destroy: function SN_destroy() {
-    this._detachEvents();
-    this.setNode(null);
-  },
-
-  setNode: function SN_setNode(value, reason="unknown") {
-    this.reason = reason;
-    if (value !== this._node) {
-      this.emit("before-new-node", value, reason);
-      let previousNode = this._node;
-      this._detachEvents();
-      this._node = value;
-      this._attachEvents();
-      this.emit("new-node", previousNode, this.reason);
-    }
-  },
-
-  get node() {
-    return this._node;
-  },
-
-  get window() {
-    if (this.isNode()) {
-      return this.node.ownerDocument.defaultView;
-    }
-    return null;
-  },
-
-  get document() {
-    if (this.isNode()) {
-      return this.node.ownerDocument;
-    }
-    return null;
-  },
-
-  isRoot: function SN_isRootNode() {
-    return this.isNode() &&
-           this.isConnected() &&
-           this.node.ownerDocument.documentElement === this.node;
-  },
-
-  isNode: function SN_isNode() {
-    return (this.node &&
-            !Components.utils.isDeadWrapper(this.node) &&
-            this.node.ownerDocument &&
-            this.node.ownerDocument.defaultView &&
-            this.node instanceof this.node.ownerDocument.defaultView.Node);
-  },
-
-  isConnected: function SN_isConnected() {
-    try {
-      let doc = this.document;
-      return doc && doc.defaultView && doc.documentElement.contains(this.node);
-    } catch (e) {
-      // "can't access dead object" error
-      return false;
-    }
-  },
-
-  isHTMLNode: function SN_isHTMLNode() {
-    let xhtml_ns = "http://www.w3.org/1999/xhtml";
-    return this.isNode() && this.node.namespaceURI == xhtml_ns;
-  },
-
-  // Node type
-
-  isElementNode: function SN_isElementNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.ELEMENT_NODE;
-  },
-
-  isAttributeNode: function SN_isAttributeNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.ATTRIBUTE_NODE;
-  },
-
-  isTextNode: function SN_isTextNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.TEXT_NODE;
-  },
-
-  isCDATANode: function SN_isCDATANode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.CDATA_SECTION_NODE;
-  },
-
-  isEntityRefNode: function SN_isEntityRefNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.ENTITY_REFERENCE_NODE;
-  },
-
-  isEntityNode: function SN_isEntityNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.ENTITY_NODE;
-  },
-
-  isProcessingInstructionNode: function SN_isProcessingInstructionNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.PROCESSING_INSTRUCTION_NODE;
-  },
-
-  isCommentNode: function SN_isCommentNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.PROCESSING_INSTRUCTION_NODE;
-  },
-
-  isDocumentNode: function SN_isDocumentNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.DOCUMENT_NODE;
-  },
-
-  isDocumentTypeNode: function SN_isDocumentTypeNode() {
-    return this.isNode() && this.node.nodeType ==this.window. Node.DOCUMENT_TYPE_NODE;
-  },
-
-  isDocumentFragmentNode: function SN_isDocumentFragmentNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.DOCUMENT_FRAGMENT_NODE;
-  },
-
-  isNotationNode: function SN_isNotationNode() {
-    return this.isNode() && this.node.nodeType == this.window.Node.NOTATION_NODE;
-  },
-}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/breadcrumbs.js
@@ -0,0 +1,597 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {Cc, Cu, Ci} = require("chrome");
+
+const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
+const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
+Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+
+const LOW_PRIORITY_ELEMENTS = {
+  "HEAD": true,
+  "BASE": true,
+  "BASEFONT": true,
+  "ISINDEX": true,
+  "LINK": true,
+  "META": true,
+  "SCRIPT": true,
+  "STYLE": true,
+  "TITLE": true,
+};
+
+///////////////////////////////////////////////////////////////////////////
+//// HTML Breadcrumbs
+
+/**
+ * Display the ancestors of the current node and its children.
+ * Only one "branch" of children are displayed (only one line).
+ *
+ * FIXME: Bug 822388 - Use the BreadcrumbsWidget in the Inspector.
+ *
+ * Mechanism:
+ * . If no nodes displayed yet:
+ *    then display the ancestor of the selected node and the selected node;
+ *   else select the node;
+ * . If the selected node is the last node displayed, append its first (if any).
+ */
+function HTMLBreadcrumbs(aInspector)
+{
+  this.inspector = aInspector;
+  this.selection = this.inspector.selection;
+  this.chromeWin = this.inspector.panelWin;
+  this.chromeDoc = this.inspector.panelDoc;
+  this.DOMHelpers = new DOMHelpers(this.chromeWin);
+  this._init();
+}
+
+exports.HTMLBreadcrumbs = HTMLBreadcrumbs;
+
+HTMLBreadcrumbs.prototype = {
+  _init: function BC__init()
+  {
+    this.container = this.chromeDoc.getElementById("inspector-breadcrumbs");
+    this.container.addEventListener("mousedown", this, true);
+    this.container.addEventListener("keypress", this, true);
+
+    // We will save a list of already displayed nodes in this array.
+    this.nodeHierarchy = [];
+
+    // Last selected node in nodeHierarchy.
+    this.currentIndex = -1;
+
+    // By default, hide the arrows. We let the <scrollbox> show them
+    // in case of overflow.
+    this.container.removeAttribute("overflows");
+    this.container._scrollButtonUp.collapsed = true;
+    this.container._scrollButtonDown.collapsed = true;
+
+    this.onscrollboxreflow = function() {
+      if (this.container._scrollButtonDown.collapsed)
+        this.container.removeAttribute("overflows");
+      else
+        this.container.setAttribute("overflows", true);
+    }.bind(this);
+
+    this.container.addEventListener("underflow", this.onscrollboxreflow, false);
+    this.container.addEventListener("overflow", this.onscrollboxreflow, false);
+
+    this.update = this.update.bind(this);
+    this.updateSelectors = this.updateSelectors.bind(this);
+    this.selection.on("new-node", this.update);
+    this.selection.on("pseudoclass", this.updateSelectors);
+    this.selection.on("attribute-changed", this.updateSelectors);
+    this.update();
+  },
+
+  /**
+   * Build a string that represents the node: tagName#id.class1.class2.
+   *
+   * @param aNode The node to pretty-print
+   * @returns a string
+   */
+  prettyPrintNodeAsText: function BC_prettyPrintNodeText(aNode)
+  {
+    let text = aNode.tagName.toLowerCase();
+    if (aNode.id) {
+      text += "#" + aNode.id;
+    }
+    for (let i = 0; i < aNode.classList.length; i++) {
+      text += "." + aNode.classList[i];
+    }
+    for (let i = 0; i < PSEUDO_CLASSES.length; i++) {
+      let pseudo = PSEUDO_CLASSES[i];
+      if (DOMUtils.hasPseudoClassLock(aNode, pseudo)) {
+        text += pseudo;
+      }
+    }
+
+    return text;
+  },
+
+
+  /**
+   * Build <label>s that represent the node:
+   *   <label class="breadcrumbs-widget-item-tag">tagName</label>
+   *   <label class="breadcrumbs-widget-item-id">#id</label>
+   *   <label class="breadcrumbs-widget-item-classes">.class1.class2</label>
+   *
+   * @param aNode The node to pretty-print
+   * @returns a document fragment.
+   */
+  prettyPrintNodeAsXUL: function BC_prettyPrintNodeXUL(aNode)
+  {
+    let fragment = this.chromeDoc.createDocumentFragment();
+
+    let tagLabel = this.chromeDoc.createElement("label");
+    tagLabel.className = "breadcrumbs-widget-item-tag plain";
+
+    let idLabel = this.chromeDoc.createElement("label");
+    idLabel.className = "breadcrumbs-widget-item-id plain";
+
+    let classesLabel = this.chromeDoc.createElement("label");
+    classesLabel.className = "breadcrumbs-widget-item-classes plain";
+
+    let pseudosLabel = this.chromeDoc.createElement("label");
+    pseudosLabel.className = "breadcrumbs-widget-item-pseudo-classes plain";
+
+    tagLabel.textContent = aNode.tagName.toLowerCase();
+    idLabel.textContent = aNode.id ? ("#" + aNode.id) : "";
+
+    let classesText = "";
+    for (let i = 0; i < aNode.classList.length; i++) {
+      classesText += "." + aNode.classList[i];
+    }
+    classesLabel.textContent = classesText;
+
+    let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
+      return DOMUtils.hasPseudoClassLock(aNode, pseudo);
+    }, this);
+    pseudosLabel.textContent = pseudos.join("");
+
+    fragment.appendChild(tagLabel);
+    fragment.appendChild(idLabel);
+    fragment.appendChild(classesLabel);
+    fragment.appendChild(pseudosLabel);
+
+    return fragment;
+  },
+
+  /**
+   * Open the sibling menu.
+   *
+   * @param aButton the button representing the node.
+   * @param aNode the node we want the siblings from.
+   */
+  openSiblingMenu: function BC_openSiblingMenu(aButton, aNode)
+  {
+    // We make sure that the targeted node is selected
+    // because we want to use the nodemenu that only works
+    // for inspector.selection
+    this.selection.setNode(aNode, "breadcrumbs");
+
+    let title = this.chromeDoc.createElement("menuitem");
+    title.setAttribute("label", this.inspector.strings.GetStringFromName("breadcrumbs.siblings"));
+    title.setAttribute("disabled", "true");
+
+    let separator = this.chromeDoc.createElement("menuseparator");
+
+    let items = [title, separator];
+
+    let nodes = aNode.parentNode.childNodes;
+    for (let i = 0; i < nodes.length; i++) {
+      if (nodes[i].nodeType == aNode.ELEMENT_NODE) {
+        let item = this.chromeDoc.createElement("menuitem");
+        if (nodes[i] === aNode) {
+          item.setAttribute("disabled", "true");
+          item.setAttribute("checked", "true");
+        }
+
+        item.setAttribute("type", "radio");
+        item.setAttribute("label", this.prettyPrintNodeAsText(nodes[i]));
+
+        let selection = this.selection;
+        item.onmouseup = (function(aNode) {
+          return function() {
+            selection.setNode(aNode, "breadcrumbs");
+          }
+        })(nodes[i]);
+
+        items.push(item);
+      }
+    }
+    this.inspector.showNodeMenu(aButton, "before_start", items);
+  },
+
+  /**
+   * Generic event handler.
+   *
+   * @param nsIDOMEvent event
+   *        The DOM event object.
+   */
+  handleEvent: function BC_handleEvent(event)
+  {
+    if (event.type == "mousedown" && event.button == 0) {
+      // on Click and Hold, open the Siblings menu
+
+      let timer;
+      let container = this.container;
+
+      function openMenu(event) {
+        cancelHold();
+        let target = event.originalTarget;
+        if (target.tagName == "button") {
+          target.onBreadcrumbsHold();
+        }
+      }
+
+      function handleClick(event) {
+        cancelHold();
+        let target = event.originalTarget;
+        if (target.tagName == "button") {
+          target.onBreadcrumbsClick();
+        }
+      }
+
+      let window = this.chromeWin;
+      function cancelHold(event) {
+        window.clearTimeout(timer);
+        container.removeEventListener("mouseout", cancelHold, false);
+        container.removeEventListener("mouseup", handleClick, false);
+      }
+
+      container.addEventListener("mouseout", cancelHold, false);
+      container.addEventListener("mouseup", handleClick, false);
+      timer = window.setTimeout(openMenu, 500, event);
+    }
+
+    if (event.type == "keypress" && this.selection.isElementNode()) {
+      let node = null;
+      switch (event.keyCode) {
+        case this.chromeWin.KeyEvent.DOM_VK_LEFT:
+          if (this.currentIndex != 0) {
+            node = this.nodeHierarchy[this.currentIndex - 1].node;
+          }
+          break;
+        case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
+          if (this.currentIndex < this.nodeHierarchy.length - 1) {
+            node = this.nodeHierarchy[this.currentIndex + 1].node;
+          }
+          break;
+        case this.chromeWin.KeyEvent.DOM_VK_UP:
+          node = this.selection.node.previousSibling;
+          while (node && (node.nodeType != node.ELEMENT_NODE)) {
+            node = node.previousSibling;
+          }
+          break;
+        case this.chromeWin.KeyEvent.DOM_VK_DOWN:
+          node = this.selection.node.nextSibling;
+          while (node && (node.nodeType != node.ELEMENT_NODE)) {
+            node = node.nextSibling;
+          }
+          break;
+      }
+      if (node) {
+        this.selection.setNode(node, "breadcrumbs");
+      }
+      event.preventDefault();
+      event.stopPropagation();
+    }
+  },
+
+  /**
+   * Remove nodes and delete properties.
+   */
+  destroy: function BC_destroy()
+  {
+    this.nodeHierarchy.forEach(function(crumb) {
+      if (LayoutHelpers.isNodeConnected(crumb.node)) {
+        DOMUtils.clearPseudoClassLocks(crumb.node);
+      }
+    });
+
+    this.selection.off("new-node", this.update);
+    this.selection.off("pseudoclass", this.updateSelectors);
+    this.selection.off("attribute-changed", this.updateSelectors);
+
+    this.container.removeEventListener("underflow", this.onscrollboxreflow, false);
+    this.container.removeEventListener("overflow", this.onscrollboxreflow, false);
+    this.onscrollboxreflow = null;
+
+    this.empty();
+    this.container.removeEventListener("mousedown", this, true);
+    this.container.removeEventListener("keypress", this, true);
+    this.container = null;
+    this.nodeHierarchy = null;
+  },
+
+  /**
+   * Empty the breadcrumbs container.
+   */
+  empty: function BC_empty()
+  {
+    while (this.container.hasChildNodes()) {
+      this.container.removeChild(this.container.firstChild);
+    }
+  },
+
+  /**
+   * Re-init the cache and remove all the buttons.
+   */
+  invalidateHierarchy: function BC_invalidateHierarchy()
+  {
+    this.inspector.hideNodeMenu();
+    this.nodeHierarchy = [];
+    this.empty();
+  },
+
+  /**
+   * Set which button represent the selected node.
+   *
+   * @param aIdx Index of the displayed-button to select
+   */
+  setCursor: function BC_setCursor(aIdx)
+  {
+    // Unselect the previously selected button
+    if (this.currentIndex > -1 && this.currentIndex < this.nodeHierarchy.length) {
+      this.nodeHierarchy[this.currentIndex].button.removeAttribute("checked");
+    }
+    if (aIdx > -1) {
+      this.nodeHierarchy[aIdx].button.setAttribute("checked", "true");
+      if (this.hadFocus)
+        this.nodeHierarchy[aIdx].button.focus();
+    }
+    this.currentIndex = aIdx;
+  },
+
+  /**
+   * Get the index of the node in the cache.
+   *
+   * @param aNode
+   * @returns integer the index, -1 if not found
+   */
+  indexOf: function BC_indexOf(aNode)
+  {
+    let i = this.nodeHierarchy.length - 1;
+    for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
+      if (this.nodeHierarchy[i].node === aNode) {
+        return i;
+      }
+    }
+    return -1;
+  },
+
+  /**
+   * Remove all the buttons and their references in the cache
+   * after a given index.
+   *
+   * @param aIdx
+   */
+  cutAfter: function BC_cutAfter(aIdx)
+  {
+    while (this.nodeHierarchy.length > (aIdx + 1)) {
+      let toRemove = this.nodeHierarchy.pop();
+      this.container.removeChild(toRemove.button);
+    }
+  },
+
+  /**
+   * Build a button representing the node.
+   *
+   * @param aNode The node from the page.
+   * @returns aNode The <button>.
+   */
+  buildButton: function BC_buildButton(aNode)
+  {
+    let button = this.chromeDoc.createElement("button");
+    button.appendChild(this.prettyPrintNodeAsXUL(aNode));
+    button.className = "breadcrumbs-widget-item";
+
+    button.setAttribute("tooltiptext", this.prettyPrintNodeAsText(aNode));
+
+    button.onkeypress = function onBreadcrumbsKeypress(e) {
+      if (e.charCode == Ci.nsIDOMKeyEvent.DOM_VK_SPACE ||
+          e.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN)
+        button.click();
+    }
+
+    button.onBreadcrumbsClick = function onBreadcrumbsClick() {
+      this.selection.setNode(aNode, "breadcrumbs");
+    }.bind(this);
+
+    button.onclick = (function _onBreadcrumbsRightClick(event) {
+      button.focus();
+      if (event.button == 2) {
+        this.openSiblingMenu(button, aNode);
+      }
+    }).bind(this);
+
+    button.onBreadcrumbsHold = (function _onBreadcrumbsHold() {
+      this.openSiblingMenu(button, aNode);
+    }).bind(this);
+    return button;
+  },
+
+  /**
+   * Connecting the end of the breadcrumbs to a node.
+   *
+   * @param aNode The node to reach.
+   */
+  expand: function BC_expand(aNode)
+  {
+      let fragment = this.chromeDoc.createDocumentFragment();
+      let toAppend = aNode;
+      let lastButtonInserted = null;
+      let originalLength = this.nodeHierarchy.length;
+      let stopNode = null;
+      if (originalLength > 0) {
+        stopNode = this.nodeHierarchy[originalLength - 1].node;
+      }
+      while (toAppend && toAppend.tagName && toAppend != stopNode) {
+        let button = this.buildButton(toAppend);
+        fragment.insertBefore(button, lastButtonInserted);
+        lastButtonInserted = button;
+        this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
+        toAppend = this.DOMHelpers.getParentObject(toAppend);
+      }
+      this.container.appendChild(fragment, this.container.firstChild);
+  },
+
+  /**
+   * Get a child of a node that can be displayed in the breadcrumbs
+   * and that is probably visible. See LOW_PRIORITY_ELEMENTS.
+   *
+   * @param aNode The parent node.
+   * @returns nsIDOMNode|null
+   */
+  getInterestingFirstNode: function BC_getInterestingFirstNode(aNode)
+  {
+    let nextChild = this.DOMHelpers.getChildObject(aNode, 0);
+    let fallback = null;
+
+    while (nextChild) {
+      if (nextChild.nodeType == aNode.ELEMENT_NODE) {
+        if (!(nextChild.tagName in LOW_PRIORITY_ELEMENTS)) {
+          return nextChild;
+        }
+        if (!fallback) {
+          fallback = nextChild;
+        }
+      }
+      nextChild = this.DOMHelpers.getNextSibling(nextChild);
+    }
+    return fallback;
+  },
+
+
+  /**
+   * Find the "youngest" ancestor of a node which is already in the breadcrumbs.
+   *
+   * @param aNode
+   * @returns Index of the ancestor in the cache
+   */
+  getCommonAncestor: function BC_getCommonAncestor(aNode)
+  {
+    let node = aNode;
+    while (node) {
+      let idx = this.indexOf(node);
+      if (idx > -1) {
+        return idx;
+      } else {
+        node = this.DOMHelpers.getParentObject(node);
+      }
+    }
+    return -1;
+  },
+
+  /**
+   * Make sure that the latest node in the breadcrumbs is not the selected node
+   * if the selected node still has children.
+   */
+  ensureFirstChild: function BC_ensureFirstChild()
+  {
+    // If the last displayed node is the selected node
+    if (this.currentIndex == this.nodeHierarchy.length - 1) {
+      let node = this.nodeHierarchy[this.currentIndex].node;
+      let child = this.getInterestingFirstNode(node);
+      // If the node has a child
+      if (child) {
+        // Show this child
+        this.expand(child);
+      }
+    }
+  },
+
+  /**
+   * Ensure the selected node is visible.
+   */
+  scroll: function BC_scroll()
+  {
+    // FIXME bug 684352: make sure its immediate neighbors are visible too.
+
+    let scrollbox = this.container;
+    let element = this.nodeHierarchy[this.currentIndex].button;
+
+    // Repeated calls to ensureElementIsVisible would interfere with each other
+    // and may sometimes result in incorrect scroll positions.
+    this.chromeWin.clearTimeout(this._ensureVisibleTimeout);
+    this._ensureVisibleTimeout = this.chromeWin.setTimeout(function() {
+      scrollbox.ensureElementIsVisible(element);
+    }, ENSURE_SELECTION_VISIBLE_DELAY);
+  },
+
+  updateSelectors: function BC_updateSelectors()
+  {
+    for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
+      let crumb = this.nodeHierarchy[i];
+      let button = crumb.button;
+
+      while(button.hasChildNodes()) {
+        button.removeChild(button.firstChild);
+      }
+      button.appendChild(this.prettyPrintNodeAsXUL(crumb.node));
+      button.setAttribute("tooltiptext", this.prettyPrintNodeAsText(crumb.node));
+    }
+  },
+
+  /**
+   * Update the breadcrumbs display when a new node is selected.
+   */
+  update: function BC_update()
+  {
+    this.inspector.hideNodeMenu();
+
+    let cmdDispatcher = this.chromeDoc.commandDispatcher;
+    this.hadFocus = (cmdDispatcher.focusedElement &&
+                     cmdDispatcher.focusedElement.parentNode == this.container);
+
+    if (!this.selection.isConnected()) {
+      this.cutAfter(-1); // remove all the crumbs
+      return;
+    }
+
+    if (!this.selection.isElementNode()) {
+      this.setCursor(-1); // no selection
+      return;
+    }
+
+    let idx = this.indexOf(this.selection.node);
+
+    // Is the node already displayed in the breadcrumbs?
+    if (idx > -1) {
+      // Yes. We select it.
+      this.setCursor(idx);
+    } else {
+      // No. Is the breadcrumbs display empty?
+      if (this.nodeHierarchy.length > 0) {
+        // No. We drop all the element that are not direct ancestors
+        // of the selection
+        let parent = this.DOMHelpers.getParentObject(this.selection.node);
+        let idx = this.getCommonAncestor(parent);
+        this.cutAfter(idx);
+      }
+      // we append the missing button between the end of the breadcrumbs display
+      // and the current node.
+      this.expand(this.selection.node);
+
+      // we select the current node button
+      idx = this.indexOf(this.selection.node);
+      this.setCursor(idx);
+    }
+    // Add the first child of the very last node of the breadcrumbs if possible.
+    this.ensureFirstChild();
+    this.updateSelectors();
+
+    // Make sure the selected node and its neighbours are visible.
+    this.scroll();
+  },
+}
+
+XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
+  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/highlighter.js
@@ -0,0 +1,798 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {Cu, Cc, Ci} = require("chrome");
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+let EventEmitter = require("devtools/shared/event-emitter");
+
+const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
+  // add ":visited" and ":link" after bug 713106 is fixed
+
+/**
+ * A highlighter mechanism.
+ *
+ * The highlighter is built dynamically into the browser element.
+ * The caller is in charge of destroying the highlighter (ie, the highlighter
+ * won't be destroyed if a new tab is selected for example).
+ *
+ * API:
+ *
+ *   // Constructor and destructor.
+ *   Highlighter(aTab, aInspector)
+ *   void destroy();
+ *
+ *   // Show and hide the highlighter
+ *   void show();
+ *   void hide();
+ *   boolean isHidden();
+ *
+ *   // Redraw the highlighter if the visible portion of the node has changed.
+ *   void invalidateSize(aScroll);
+ *
+ * Events:
+ *
+ *   "closed" - Highlighter is closing
+ *   "highlighting" - Highlighter is highlighting
+ *   "locked" - The selected node has been locked
+ *   "unlocked" - The selected ndoe has been unlocked
+ *
+ * Structure:
+ *  <stack class="highlighter-container">
+ *    <box class="highlighter-outline-container">
+ *      <box class="highlighter-outline" locked="true/false"/>
+ *    </box>
+ *    <box class="highlighter-controls">
+ *      <box class="highlighter-nodeinfobar-container" position="top/bottom" locked="true/false">
+ *        <box class="highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-top"/>
+ *        <hbox class="highlighter-nodeinfobar">
+ *          <toolbarbutton class="highlighter-nodeinfobar-inspectbutton highlighter-nodeinfobar-button"/>
+ *          <hbox class="highlighter-nodeinfobar-text">tagname#id.class1.class2</hbox>
+ *          <toolbarbutton class="highlighter-nodeinfobar-menu highlighter-nodeinfobar-button">…</toolbarbutton>
+ *        </hbox>
+ *        <box class="highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-bottom"/>
+ *      </box>
+ *    </box>
+ *  </stack>
+ *
+ */
+
+
+/**
+ * Constructor.
+ *
+ * @param aTarget The inspection target.
+ * @param aInspector Inspector panel.
+ * @param aToolbox The toolbox holding the inspector.
+ */
+function Highlighter(aTarget, aInspector, aToolbox)
+{
+  this.target = aTarget;
+  this.tab = aTarget.tab;
+  this.toolbox = aToolbox;
+  this.browser = this.tab.linkedBrowser;
+  this.chromeDoc = this.tab.ownerDocument;
+  this.chromeWin = this.chromeDoc.defaultView;
+  this.inspector = aInspector
+
+  EventEmitter.decorate(this);
+
+  this._init();
+}
+
+exports.Highlighter = Highlighter;
+
+Highlighter.prototype = {
+  get selection() {
+    return this.inspector.selection;
+  },
+
+  _init: function Highlighter__init()
+  {
+    this.unlockAndFocus = this.unlockAndFocus.bind(this);
+    this.updateInfobar = this.updateInfobar.bind(this);
+    this.highlight = this.highlight.bind(this);
+
+    let stack = this.browser.parentNode;
+    this.win = this.browser.contentWindow;
+    this._highlighting = false;
+
+    this.highlighterContainer = this.chromeDoc.createElement("stack");
+    this.highlighterContainer.className = "highlighter-container";
+
+    this.outline = this.chromeDoc.createElement("box");
+    this.outline.className = "highlighter-outline";
+
+    let outlineContainer = this.chromeDoc.createElement("box");
+    outlineContainer.appendChild(this.outline);
+    outlineContainer.className = "highlighter-outline-container";
+
+    // The controlsBox will host the different interactive
+    // elements of the highlighter (buttons, toolbars, ...).
+    let controlsBox = this.chromeDoc.createElement("box");
+    controlsBox.className = "highlighter-controls";
+    this.highlighterContainer.appendChild(outlineContainer);
+    this.highlighterContainer.appendChild(controlsBox);
+
+    // Insert the highlighter right after the browser
+    stack.insertBefore(this.highlighterContainer, stack.childNodes[1]);
+
+    this.buildInfobar(controlsBox);
+
+    this.transitionDisabler = null;
+    this.pageEventsMuter = null;
+
+    this.unlockAndFocus();
+
+    this.selection.on("new-node", this.highlight);
+    this.selection.on("new-node", this.updateInfobar);
+    this.selection.on("pseudoclass", this.updateInfobar);
+    this.selection.on("attribute-changed", this.updateInfobar);
+
+    this.onToolSelected = function(event, id) {
+      if (id != "inspector") {
+        this.chromeWin.clearTimeout(this.pageEventsMuter);
+        this.detachMouseListeners();
+        this.disabled = true;
+        this.hide();
+      } else {
+        if (!this.locked) {
+          this.attachMouseListeners();
+        }
+        this.disabled = false;
+        this.show();
+      }
+    }.bind(this);
+    this.toolbox.on("select", this.onToolSelected);
+
+    this.hidden = true;
+    this.highlight();
+  },
+
+  /**
+   * Destroy the nodes. Remove listeners.
+   */
+  destroy: function Highlighter_destroy()
+  {
+    this.inspectButton.removeEventListener("command", this.unlockAndFocus);
+    this.inspectButton = null;
+
+    this.toolbox.off("select", this.onToolSelected);
+    this.toolbox = null;
+
+    this.selection.off("new-node", this.highlight);
+    this.selection.off("new-node", this.updateInfobar);
+    this.selection.off("pseudoclass", this.updateInfobar);
+    this.selection.off("attribute-changed", this.updateInfobar);
+
+    this.detachMouseListeners();
+    this.detachPageListeners();
+
+    this.chromeWin.clearTimeout(this.transitionDisabler);
+    this.chromeWin.clearTimeout(this.pageEventsMuter);
+    this.boundCloseEventHandler = null;
+    this._contentRect = null;
+    this._highlightRect = null;
+    this._highlighting = false;
+    this.outline = null;
+    this.nodeInfo = null;
+    this.highlighterContainer.parentNode.removeChild(this.highlighterContainer);
+    this.highlighterContainer = null;
+    this.win = null
+    this.browser = null;
+    this.chromeDoc = null;
+    this.chromeWin = null;
+    this.tabbrowser = null;
+
+    this.emit("closed");
+  },
+
+  /**
+   * Show the outline, and select a node.
+   */
+  highlight: function Highlighter_highlight()
+  {
+    if (this.selection.reason != "highlighter") {
+      this.lock();
+    }
+
+    let canHighlightNode = this.selection.isNode() &&
+                          this.selection.isConnected() &&
+                          this.selection.isElementNode();
+
+    if (canHighlightNode) {
+      if (this.selection.reason != "navigateaway") {
+        this.disabled = false;
+      }
+      this.show();
+      this.updateInfobar();
+      this.invalidateSize();
+      if (!this._highlighting &&
+          this.selection.reason != "highlighter") {
+        LayoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
+      }
+    } else {
+      this.disabled = true;
+      this.hide();
+    }
+  },
+
+  /**
+   * Update the highlighter size and position.
+   */
+  invalidateSize: function Highlighter_invalidateSize()
+  {
+    let canHiglightNode = this.selection.isNode() &&
+                          this.selection.isConnected() &&
+                          this.selection.isElementNode();
+
+    if (!canHiglightNode)
+      return;
+
+    let clientRect = this.selection.node.getBoundingClientRect();
+    let rect = LayoutHelpers.getDirtyRect(this.selection.node);
+    this.highlightRectangle(rect);
+
+    this.moveInfobar();
+
+    if (this._highlighting) {
+      this.showOutline();
+      this.emit("highlighting");
+    }
+  },
+
+  /**
+   * Show the highlighter if it has been hidden.
+   */
+  show: function() {
+    if (!this.hidden || this.disabled) return;
+    this.showOutline();
+    this.showInfobar();
+    this.computeZoomFactor();
+    this.attachPageListeners();
+    this.invalidateSize();
+    this.hidden = false;
+  },
+
+  /**
+   * Hide the highlighter, the outline and the infobar.
+   */
+  hide: function() {
+    if (this.hidden) return;
+    this.hideOutline();
+    this.hideInfobar();
+    this.detachPageListeners();
+    this.hidden = true;
+  },
+
+  /**
+   * Is the highlighter visible?
+   *
+   * @return boolean
+   */
+  isHidden: function() {
+    return this.hidden;
+  },
+
+  /**
+   * Lock a node. Stops the inspection.
+   */
+  lock: function() {
+    if (this.locked === true) return;
+    this.outline.setAttribute("locked", "true");
+    this.nodeInfo.container.setAttribute("locked", "true");
+    this.detachMouseListeners();
+    this.locked = true;
+    this.emit("locked");
+  },
+
+  /**
+   * Start inspecting.
+   * Unlock the current node (if any), and select any node being hovered.
+   */
+  unlock: function() {
+    if (this.locked === false) return;
+    this.outline.removeAttribute("locked");
+    this.nodeInfo.container.removeAttribute("locked");
+    this.attachMouseListeners();
+    this.locked = false;
+    if (this.selection.isElementNode() &&
+        this.selection.isConnected()) {
+      this.showOutline();
+    }
+    this.emit("unlocked");
+  },
+
+  /**
+   * Focus the browser before unlocking.
+   */
+  unlockAndFocus: function Highlighter_unlockAndFocus() {
+    if (this.locked === false) return;
+    this.chromeWin.focus();
+    this.unlock();
+  },
+
+  /**
+   * Hide the infobar
+   */
+   hideInfobar: function Highlighter_hideInfobar() {
+     this.nodeInfo.container.setAttribute("force-transitions", "true");
+     this.nodeInfo.container.setAttribute("hidden", "true");
+   },
+
+  /**
+   * Show the infobar
+   */
+   showInfobar: function Highlighter_showInfobar() {
+     this.nodeInfo.container.removeAttribute("hidden");
+     this.moveInfobar();
+     this.nodeInfo.container.removeAttribute("force-transitions");
+   },
+
+  /**
+   * Hide the outline
+   */
+   hideOutline: function Highlighter_hideOutline() {
+     this.outline.setAttribute("hidden", "true");
+   },
+
+  /**
+   * Show the outline
+   */
+   showOutline: function Highlighter_showOutline() {
+     if (this._highlighting)
+       this.outline.removeAttribute("hidden");
+   },
+
+  /**
+   * Build the node Infobar.
+   *
+   * <box class="highlighter-nodeinfobar-container">
+   *   <box class="Highlighter-nodeinfobar-arrow-top"/>
+   *   <hbox class="highlighter-nodeinfobar">
+   *     <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"/>
+   *     <hbox class="highlighter-nodeinfobar-text">
+   *       <xhtml:span class="highlighter-nodeinfobar-tagname"/>
+   *       <xhtml:span class="highlighter-nodeinfobar-id"/>
+   *       <xhtml:span class="highlighter-nodeinfobar-classes"/>
+   *       <xhtml:span class="highlighter-nodeinfobar-pseudo-classes"/>
+   *     </hbox>
+   *     <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"/>
+   *   </hbox>
+   *   <box class="Highlighter-nodeinfobar-arrow-bottom"/>
+   * </box>
+   *
+   * @param nsIDOMElement aParent
+   *        The container of the infobar.
+   */
+  buildInfobar: function Highlighter_buildInfobar(aParent)
+  {
+    let container = this.chromeDoc.createElement("box");
+    container.className = "highlighter-nodeinfobar-container";
+    container.setAttribute("position", "top");
+    container.setAttribute("disabled", "true");
+
+    let nodeInfobar = this.chromeDoc.createElement("hbox");
+    nodeInfobar.className = "highlighter-nodeinfobar";
+
+    let arrowBoxTop = this.chromeDoc.createElement("box");
+    arrowBoxTop.className = "highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-top";
+
+    let arrowBoxBottom = this.chromeDoc.createElement("box");
+    arrowBoxBottom.className = "highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-bottom";
+
+    let tagNameLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
+    tagNameLabel.className = "highlighter-nodeinfobar-tagname";
+
+    let idLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
+    idLabel.className = "highlighter-nodeinfobar-id";
+
+    let classesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
+    classesBox.className = "highlighter-nodeinfobar-classes";
+
+    let pseudoClassesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
+    pseudoClassesBox.className = "highlighter-nodeinfobar-pseudo-classes";
+
+    // Add some content to force a better boundingClientRect down below.
+    pseudoClassesBox.textContent = "&nbsp;";
+
+    // Create buttons
+
+    this.inspectButton = this.chromeDoc.createElement("toolbarbutton");
+    this.inspectButton.className = "highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"
+    let toolbarInspectButton = this.inspector.panelDoc.getElementById("inspector-inspect-toolbutton");
+    this.inspectButton.setAttribute("tooltiptext", toolbarInspectButton.getAttribute("tooltiptext"));
+    this.inspectButton.addEventListener("command", this.unlockAndFocus);
+
+    let nodemenu = this.chromeDoc.createElement("toolbarbutton");
+    nodemenu.setAttribute("type", "menu");
+    nodemenu.className = "highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"
+    nodemenu.setAttribute("tooltiptext",
+                          this.strings.GetStringFromName("nodeMenu.tooltiptext"));
+
+    nodemenu.onclick = function() {
+      this.inspector.showNodeMenu(nodemenu, "after_start");
+    }.bind(this);
+
+    // <hbox class="highlighter-nodeinfobar-text"/>
+    let texthbox = this.chromeDoc.createElement("hbox");
+    texthbox.className = "highlighter-nodeinfobar-text";
+    texthbox.setAttribute("align", "center");
+    texthbox.setAttribute("flex", "1");
+
+    texthbox.addEventListener("mousedown", function(aEvent) {
+      // On click, show the node:
+      if (this.selection.isElementNode()) {
+        LayoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
+      }
+    }.bind(this), true);
+
+    texthbox.appendChild(tagNameLabel);
+    texthbox.appendChild(idLabel);
+    texthbox.appendChild(classesBox);
+    texthbox.appendChild(pseudoClassesBox);
+
+    nodeInfobar.appendChild(this.inspectButton);
+    nodeInfobar.appendChild(texthbox);
+    nodeInfobar.appendChild(nodemenu);
+
+    container.appendChild(arrowBoxTop);
+    container.appendChild(nodeInfobar);
+    container.appendChild(arrowBoxBottom);
+
+    aParent.appendChild(container);
+
+    let barHeight = container.getBoundingClientRect().height;
+
+    this.nodeInfo = {
+      tagNameLabel: tagNameLabel,
+      idLabel: idLabel,
+      classesBox: classesBox,
+      pseudoClassesBox: pseudoClassesBox,
+      container: container,
+      barHeight: barHeight,
+    };
+  },
+
+  /**
+   * Highlight a rectangular region.
+   *
+   * @param object aRect
+   *        The rectangle region to highlight.
+   * @returns boolean
+   *          True if the rectangle was highlighted, false otherwise.
+   */
+  highlightRectangle: function Highlighter_highlightRectangle(aRect)
+  {
+    if (!aRect) {
+      this.unhighlight();
+      return;
+    }
+
+    let oldRect = this._contentRect;
+
+    if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
+        aRect.width == oldRect.width && aRect.height == oldRect.height) {
+      return; // same rectangle
+    }
+
+    let aRectScaled = LayoutHelpers.getZoomedRect(this.win, aRect);
+
+    if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
+        aRectScaled.width > 0 && aRectScaled.height > 0) {
+
+      this.showOutline();
+
+      // The bottom div and the right div are flexibles (flex=1).
+      // We don't need to resize them.
+      let top = "top:" + aRectScaled.top + "px;";
+      let left = "left:" + aRectScaled.left + "px;";
+      let width = "width:" + aRectScaled.width + "px;";
+      let height = "height:" + aRectScaled.height + "px;";
+      this.outline.setAttribute("style", top + left + width + height);
+
+      this._highlighting = true;
+    } else {
+      this.unhighlight();
+    }
+
+    this._contentRect = aRect; // save orig (non-scaled) rect
+    this._highlightRect = aRectScaled; // and save the scaled rect.
+
+    return;
+  },
+
+  /**
+   * Clear the highlighter surface.
+   */
+  unhighlight: function Highlighter_unhighlight()
+  {
+    this._highlighting = false;
+    this.hideOutline();
+  },
+
+  /**
+   * Update node information (tagName#id.class)
+   */
+  updateInfobar: function Highlighter_updateInfobar()
+  {
+    if (!this.selection.isElementNode()) {
+      this.nodeInfo.tagNameLabel.textContent = "";
+      this.nodeInfo.idLabel.textContent = "";
+      this.nodeInfo.classesBox.textContent = "";
+      this.nodeInfo.pseudoClassesBox.textContent = "";
+      return;
+    }
+
+    let node = this.selection.node;
+
+    // Tag name
+    this.nodeInfo.tagNameLabel.textContent = node.tagName;
+
+    // ID
+    this.nodeInfo.idLabel.textContent = node.id ? "#" + node.id : "";
+
+    // Classes
+    let classes = this.nodeInfo.classesBox;
+
+    classes.textContent = node.classList.length ?
+                            "." + Array.join(node.classList, ".") : "";
+
+    // Pseudo-classes
+    let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
+      return DOMUtils.hasPseudoClassLock(node, pseudo);
+    }, this);
+
+    let pseudoBox = this.nodeInfo.pseudoClassesBox;
+    pseudoBox.textContent = pseudos.join("");
+  },
+
+  /**
+   * Move the Infobar to the right place in the highlighter.
+   */
+  moveInfobar: function Highlighter_moveInfobar()
+  {
+    if (this._highlightRect) {
+      let winHeight = this.win.innerHeight * this.zoom;
+      let winWidth = this.win.innerWidth * this.zoom;
+
+      let rect = {top: this._highlightRect.top,
+                  left: this._highlightRect.left,
+                  width: this._highlightRect.width,
+                  height: this._highlightRect.height};
+
+      rect.top = Math.max(rect.top, 0);
+      rect.left = Math.max(rect.left, 0);
+      rect.width = Math.max(rect.width, 0);
+      rect.height = Math.max(rect.height, 0);
+
+      rect.top = Math.min(rect.top, winHeight);
+      rect.left = Math.min(rect.left, winWidth);
+
+      this.nodeInfo.container.removeAttribute("disabled");
+      // Can the bar be above the node?
+      if (rect.top < this.nodeInfo.barHeight) {
+        // No. Can we move the toolbar under the node?
+        if (rect.top + rect.height +
+            this.nodeInfo.barHeight > winHeight) {
+          // No. Let's move it inside.
+          this.nodeInfo.container.style.top = rect.top + "px";
+          this.nodeInfo.container.setAttribute("position", "overlap");
+        } else {
+          // Yes. Let's move it under the node.
+          this.nodeInfo.container.style.top = rect.top + rect.height + "px";
+          this.nodeInfo.container.setAttribute("position", "bottom");
+        }
+      } else {
+        // Yes. Let's move it on top of the node.
+        this.nodeInfo.container.style.top =
+          rect.top - this.nodeInfo.barHeight + "px";
+        this.nodeInfo.container.setAttribute("position", "top");
+      }
+
+      let barWidth = this.nodeInfo.container.getBoundingClientRect().width;
+      let left = rect.left + rect.width / 2 - barWidth / 2;
+
+      // Make sure the whole infobar is visible
+      if (left < 0) {
+        left = 0;
+        this.nodeInfo.container.setAttribute("hide-arrow", "true");
+      } else {
+        if (left + barWidth > winWidth) {
+          left = winWidth - barWidth;
+          this.nodeInfo.container.setAttribute("hide-arrow", "true");
+        } else {
+          this.nodeInfo.container.removeAttribute("hide-arrow");
+        }
+      }
+      this.nodeInfo.container.style.left = left + "px";
+    } else {
+      this.nodeInfo.container.style.left = "0";
+      this.nodeInfo.container.style.top = "0";
+      this.nodeInfo.container.setAttribute("position", "top");
+      this.nodeInfo.container.setAttribute("hide-arrow", "true");
+    }
+  },
+
+  /**
+   * Store page zoom factor.
+   */
+  computeZoomFactor: function Highlighter_computeZoomFactor() {
+    this.zoom =
+      this.win.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDOMWindowUtils)
+      .fullZoom;
+  },
+
+  /////////////////////////////////////////////////////////////////////////
+  //// Event Handling
+
+  attachMouseListeners: function Highlighter_attachMouseListeners()
+  {
+    this.browser.addEventListener("mousemove", this, true);
+    this.browser.addEventListener("click", this, true);
+    this.browser.addEventListener("dblclick", this, true);
+    this.browser.addEventListener("mousedown", this, true);
+    this.browser.addEventListener("mouseup", this, true);
+  },
+
+  detachMouseListeners: function Highlighter_detachMouseListeners()
+  {
+    this.browser.removeEventListener("mousemove", this, true);
+    this.browser.removeEventListener("click", this, true);
+    this.browser.removeEventListener("dblclick", this, true);
+    this.browser.removeEventListener("mousedown", this, true);
+    this.browser.removeEventListener("mouseup", this, true);
+  },
+
+  attachPageListeners: function Highlighter_attachPageListeners()
+  {
+    this.browser.addEventListener("resize", this, true);
+    this.browser.addEventListener("scroll", this, true);
+    this.browser.addEventListener("MozAfterPaint", this, true);
+  },
+
+  detachPageListeners: function Highlighter_detachPageListeners()
+  {
+    this.browser.removeEventListener("resize", this, true);
+    this.browser.removeEventListener("scroll", this, true);
+    this.browser.removeEventListener("MozAfterPaint", this, true);
+  },
+
+  /**
+   * Generic event handler.
+   *
+   * @param nsIDOMEvent aEvent
+   *        The DOM event object.
+   */
+  handleEvent: function Highlighter_handleEvent(aEvent)
+  {
+    switch (aEvent.type) {
+      case "click":
+        this.handleClick(aEvent);
+        break;
+      case "mousemove":
+        this.brieflyIgnorePageEvents();
+        this.handleMouseMove(aEvent);
+        break;
+      case "resize":
+        this.computeZoomFactor();
+        break;
+      case "MozAfterPaint":
+      case "scroll":
+        this.brieflyDisableTransitions();
+        this.invalidateSize();
+        break;
+      case "dblclick":
+      case "mousedown":
+      case "mouseup":
+        aEvent.stopPropagation();
+        aEvent.preventDefault();
+        break;
+    }
+  },
+
+  /**
+   * Disable the CSS transitions for a short time to avoid laggy animations
+   * during scrolling or resizing.
+   */
+  brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
+  {
+    if (this.transitionDisabler) {
+      this.chromeWin.clearTimeout(this.transitionDisabler);
+    } else {
+      this.outline.setAttribute("disable-transitions", "true");
+      this.nodeInfo.container.setAttribute("disable-transitions", "true");
+    }
+    this.transitionDisabler =
+      this.chromeWin.setTimeout(function() {
+        this.outline.removeAttribute("disable-transitions");
+        this.nodeInfo.container.removeAttribute("disable-transitions");
+        this.transitionDisabler = null;
+      }.bind(this), 500);
+  },
+
+  /**
+   * Don't listen to page events while inspecting with the mouse.
+   */
+  brieflyIgnorePageEvents: function Highlighter_brieflyIgnorePageEvents()
+  {
+    // The goal is to keep smooth animations while inspecting.
+    // CSS Transitions might be interrupted because of a MozAfterPaint
+    // event that would triger an invalidateSize() call.
+    // So we don't listen to events that would trigger an invalidateSize()
+    // call.
+    //
+    // Side effect, zoom levels are not updated during this short period.
+    // It's very unlikely this would happen, but just in case, we call
+    // computeZoomFactor() when reattaching the events.
+    if (this.pageEventsMuter) {
+      this.chromeWin.clearTimeout(this.pageEventsMuter);
+    } else {
+      this.detachPageListeners();
+    }
+    this.pageEventsMuter =
+      this.chromeWin.setTimeout(function() {
+        this.attachPageListeners();
+        // Just in case the zoom level changed while ignoring the paint events
+        this.computeZoomFactor();
+        this.pageEventsMuter = null;
+      }.bind(this), 500);
+  },
+
+  /**
+   * Handle clicks.
+   *
+   * @param nsIDOMEvent aEvent
+   *        The DOM event.
+   */
+  handleClick: function Highlighter_handleClick(aEvent)
+  {
+    // Stop inspection when the user clicks on a node.
+    if (aEvent.button == 0) {
+      this.lock();
+      let node = this.selection.node;
+      this.selection.setNode(node, "highlighter-lock");
+      aEvent.preventDefault();
+      aEvent.stopPropagation();
+    }
+  },
+
+  /**
+   * Handle mousemoves in panel.
+   *
+   * @param nsiDOMEvent aEvent
+   *        The MouseEvent triggering the method.
+   */
+  handleMouseMove: function Highlighter_handleMouseMove(aEvent)
+  {
+    let doc = aEvent.target.ownerDocument;
+
+    // This should never happen, but just in case, we don't let the
+    // highlighter highlight browser nodes.
+    if (doc && doc != this.chromeDoc) {
+      let element = LayoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
+        aEvent.clientX, aEvent.clientY);
+      if (element && element != this.selection.node) {
+        this.selection.setNode(element, "highlighter");
+      }
+    }
+  },
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
+  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
+});
+
+XPCOMUtils.defineLazyGetter(Highlighter.prototype, "strings", function () {
+    return Services.strings.createBundle(
+            "chrome://browser/locale/devtools/inspector.properties");
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -0,0 +1,634 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {Cc, Ci, Cu, Cr} = require("chrome");
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+let Promise = require("sdk/core/promise");
+let EventEmitter = require("devtools/shared/event-emitter");
+let {CssLogic} = require("devtools/styleinspector/css-logic");
+
+loader.lazyGetter(this, "MarkupView", () => require("devtools/markupview/markup-view").MarkupView);
+loader.lazyGetter(this, "Selection", () => require ("devtools/inspector/selection").Selection);
+loader.lazyGetter(this, "HTMLBreadcrumbs", () => require("devtools/inspector/breadcrumbs").HTMLBreadcrumbs);
+loader.lazyGetter(this, "Highlighter", () => require("devtools/inspector/highlighter").Highlighter);
+loader.lazyGetter(this, "ToolSidebar", () => require("devtools/framework/sidebar").ToolSidebar);
+loader.lazyGetter(this, "SelectorSearch", () => require("devtools/inspector/selector-search").SelectorSearch);
+
+const LAYOUT_CHANGE_TIMER = 250;
+
+/**
+ * Represents an open instance of the Inspector for a tab.
+ * The inspector controls the highlighter, the breadcrumbs,
+ * the markup view, and the sidebar (computed view, rule view
+ * and layout view).
+ */
+function InspectorPanel(iframeWindow, toolbox) {
+  this._toolbox = toolbox;
+  this._target = toolbox._target;
+  this.panelDoc = iframeWindow.document;
+  this.panelWin = iframeWindow;
+  this.panelWin.inspector = this;
+
+  EventEmitter.decorate(this);
+}
+
+exports.InspectorPanel = InspectorPanel;
+
+InspectorPanel.prototype = {
+  /**
+   * open is effectively an asynchronous constructor
+   */
+  open: function InspectorPanel_open() {
+    let deferred = Promise.defer();
+
+    this.onNavigatedAway = this.onNavigatedAway.bind(this);
+    this.target.on("navigate", this.onNavigatedAway);
+
+    this.nodemenu = this.panelDoc.getElementById("inspector-node-popup");
+    this.lastNodemenuItem = this.nodemenu.lastChild;
+    this._setupNodeMenu = this._setupNodeMenu.bind(this);
+    this._resetNodeMenu = this._resetNodeMenu.bind(this);
+    this.nodemenu.addEventListener("popupshowing", this._setupNodeMenu, true);
+    this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
+
+    // Create an empty selection
+    this._selection = new Selection();
+    this.onNewSelection = this.onNewSelection.bind(this);
+    this.selection.on("new-node", this.onNewSelection);
+    this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
+    this.selection.on("before-new-node", this.onBeforeNewSelection);
+    this.onDetached = this.onDetached.bind(this);
+    this.selection.on("detached", this.onDetached);
+
+    this.breadcrumbs = new HTMLBreadcrumbs(this);
+
+    if (this.target.isLocalTab) {
+      this.browser = this.target.tab.linkedBrowser;
+      this.scheduleLayoutChange = this.scheduleLayoutChange.bind(this);
+      this.browser.addEventListener("resize", this.scheduleLayoutChange, true);
+
+      this.highlighter = new Highlighter(this.target, this, this._toolbox);
+      let button = this.panelDoc.getElementById("inspector-inspect-toolbutton");
+      button.hidden = false;
+      this.onLockStateChanged = function() {
+        if (this.highlighter.locked) {
+          button.removeAttribute("checked");
+          this._toolbox.raise();
+        } else {
+          button.setAttribute("checked", "true");
+        }
+      }.bind(this);
+      this.highlighter.on("locked", this.onLockStateChanged);
+      this.highlighter.on("unlocked", this.onLockStateChanged);
+
+      // Show a warning when the debugger is paused.
+      // We show the warning only when the inspector
+      // is selected.
+      this.updateDebuggerPausedWarning = function() {
+        let notificationBox = this._toolbox.getNotificationBox();
+        let notification = notificationBox.getNotificationWithValue("inspector-script-paused");
+        if (!notification && this._toolbox.currentToolId == "inspector" &&
+            this.target.isThreadPaused) {
+          let message = this.strings.GetStringFromName("debuggerPausedWarning.message");
+          notificationBox.appendNotification(message,
+            "inspector-script-paused", "", notificationBox.PRIORITY_WARNING_HIGH);
+        }
+
+        if (notification && this._toolbox.currentToolId != "inspector") {
+          notificationBox.removeNotification(notification);
+        }
+
+        if (notification && !this.target.isThreadPaused) {
+          notificationBox.removeNotification(notification);
+        }
+
+      }.bind(this);
+      this.target.on("thread-paused", this.updateDebuggerPausedWarning);
+      this.target.on("thread-resumed", this.updateDebuggerPausedWarning);
+      this._toolbox.on("select", this.updateDebuggerPausedWarning);
+      this.updateDebuggerPausedWarning();
+    }
+
+    this._initMarkup();
+    this.isReady = false;
+
+    this.once("markuploaded", function() {
+      this.isReady = true;
+
+      // All the components are initialized. Let's select a node.
+      if (this.target.isLocalTab) {
+        let root = this.browser.contentDocument.documentElement;
+        this._selection.setNode(root);
+      } else if (this.target.window) {
+        let root = this.target.window.document.documentElement;
+        this._selection.setNode(root);
+      }
+
+      if (this.highlighter) {
+        this.highlighter.unlock();
+      }
+
+      this.emit("ready");
+      deferred.resolve(this);
+    }.bind(this));
+
+    this.setupSearchBox();
+    this.setupSidebar();
+
+    return deferred.promise;
+  },
+
+  /**
+   * Selection object (read only)
+   */
+  get selection() {
+    return this._selection;
+  },
+
+  /**
+   * Target getter.
+   */
+  get target() {
+    return this._target;
+  },
+
+  /**
+   * Target setter.
+   */
+  set target(value) {
+    this._target = value;
+  },
+
+  /**
+   * Expose gViewSourceUtils so that other tools can make use of them.
+   */
+  get viewSourceUtils() {
+    return this.panelWin.gViewSourceUtils;
+  },
+
+  /**
+   * Indicate that a tool has modified the state of the page.  Used to
+   * decide whether to show the "are you sure you want to navigate"
+   * notification.
+   */
+  markDirty: function InspectorPanel_markDirty() {
+    this.isDirty = true;
+  },
+
+  /**
+   * Hooks the searchbar to show result and auto completion suggestions.
+   */
+  setupSearchBox: function InspectorPanel_setupSearchBox() {
+    // Initiate the selectors search object.
+    let setNodeFunction = function(node) {
+      this.selection.setNode(node, "selectorsearch");
+    }.bind(this);
+    if (this.searchSuggestions) {
+      this.searchSuggestions.destroy();
+      this.searchSuggestions = null;
+    }
+    this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
+    this.searchSuggestions = new SelectorSearch(this.browser.contentDocument,
+                                                this.searchBox,
+                                                setNodeFunction);
+  },
+
+  /**
+   * Build the sidebar.
+   */
+  setupSidebar: function InspectorPanel_setupSidebar() {
+    let tabbox = this.panelDoc.querySelector("#inspector-sidebar");
+    this.sidebar = new ToolSidebar(tabbox, this);
+
+    let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
+
+    this._setDefaultSidebar = function(event, toolId) {
+      Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);
+    }.bind(this);
+
+    this.sidebar.on("select", this._setDefaultSidebar);
+    this.toggleHighlighter = this.toggleHighlighter.bind(this);
+
+    this.sidebar.addTab("ruleview",
+                        "chrome://browser/content/devtools/cssruleview.xhtml",
+                        "ruleview" == defaultTab);
+
+    this.sidebar.addTab("computedview",
+                        "chrome://browser/content/devtools/computedview.xhtml",
+                        "computedview" == defaultTab);
+
+    if (Services.prefs.getBoolPref("devtools.fontinspector.enabled")) {
+      this.sidebar.addTab("fontinspector",
+                          "chrome://browser/content/devtools/fontinspector/font-inspector.xhtml",
+                          "fontinspector" == defaultTab);
+    }
+
+    this.sidebar.addTab("layoutview",
+                        "chrome://browser/content/devtools/layoutview/view.xhtml",
+                        "layoutview" == defaultTab);
+
+    let ruleViewTab = this.sidebar.getTab("ruleview");
+    ruleViewTab.addEventListener("mouseover", this.toggleHighlighter, false);
+    ruleViewTab.addEventListener("mouseout", this.toggleHighlighter, false);
+
+    this.sidebar.show();
+  },
+
+  /**
+   * Reset the inspector on navigate away.
+   */
+  onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
+    let newWindow = payload._navPayload || payload;
+    this.selection.setNode(null);
+    this._destroyMarkup();
+    this.isDirty = false;
+    let self = this;
+
+    function onDOMReady() {
+      newWindow.removeEventListener("DOMContentLoaded", onDOMReady, true);
+
+      if (self._destroyed) {
+        return;
+      }
+
+      if (!self.selection.node) {
+        self.selection.setNode(newWindow.document.documentElement, "navigateaway");
+      }
+      self._initMarkup();
+      self.setupSearchBox();
+    }
+
+    if (newWindow.document.readyState == "loading") {
+      newWindow.addEventListener("DOMContentLoaded", onDOMReady, true);
+    } else {
+      onDOMReady();
+    }
+  },
+
+  /**
+   * When a new node is selected.
+   */
+  onNewSelection: function InspectorPanel_onNewSelection() {
+    this.cancelLayoutChange();
+  },
+
+  /**
+   * When a new node is selected, before the selection has changed.
+   */
+  onBeforeNewSelection: function InspectorPanel_onBeforeNewSelection(event,
+                                                                     node) {
+    if (this.breadcrumbs.indexOf(node) == -1) {
+      // only clear locks if we'd have to update breadcrumbs
+      this.clearPseudoClasses();
+    }
+  },
+
+  /**
+   * When a node is deleted, select its parent node.
+   */
+  onDetached: function InspectorPanel_onDetached(event, parentNode) {
+    this.cancelLayoutChange();
+    this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
+    this.selection.setNode(parentNode, "detached");
+  },
+
+  /**
+   * Destroy the inspector.
+   */
+  destroy: function InspectorPanel__destroy() {
+    if (this._destroyed) {
+      return Promise.resolve(null);
+    }
+    this._destroyed = true;
+
+    this.cancelLayoutChange();
+
+    if (this.browser) {
+      this.browser.removeEventListener("resize", this.scheduleLayoutChange, true);
+      this.browser = null;
+    }
+
+    this.target.off("navigate", this.onNavigatedAway);
+
+    if (this.highlighter) {
+      this.highlighter.off("locked", this.onLockStateChanged);
+      this.highlighter.off("unlocked", this.onLockStateChanged);
+      this.highlighter.destroy();
+    }
+
+    this.target.off("thread-paused", this.updateDebuggerPausedWarning);
+    this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
+    this._toolbox.off("select", this.updateDebuggerPausedWarning);
+
+    this._toolbox = null;
+
+    this.sidebar.off("select", this._setDefaultSidebar);
+    this.sidebar.destroy();
+    this.sidebar = null;
+
+    this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
+    this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
+    this.breadcrumbs.destroy();
+    this.searchSuggestions.destroy();
+    this.selection.off("new-node", this.onNewSelection);
+    this.selection.off("before-new-node", this.onBeforeNewSelection);
+    this.selection.off("detached", this.onDetached);
+    this._destroyMarkup();
+    this._selection.destroy();
+    this._selection = null;
+    this.panelWin.inspector = null;
+    this.target = null;
+    this.panelDoc = null;
+    this.panelWin = null;
+    this.breadcrumbs = null;
+    this.searchSuggestions = null;
+    this.lastNodemenuItem = null;
+    this.nodemenu = null;
+    this.highlighter = null;
+
+    return Promise.resolve(null);
+  },
+
+  /**
+   * Show the node menu.
+   */
+  showNodeMenu: function InspectorPanel_showNodeMenu(aButton, aPosition, aExtraItems) {
+    if (aExtraItems) {
+      for (let item of aExtraItems) {
+        this.nodemenu.appendChild(item);
+      }
+    }
+    this.nodemenu.openPopup(aButton, aPosition, 0, 0, true, false);
+  },
+
+  hideNodeMenu: function InspectorPanel_hideNodeMenu() {
+    this.nodemenu.hidePopup();
+  },
+
+  /**
+   * Disable the delete item if needed. Update the pseudo classes.
+   */
+  _setupNodeMenu: function InspectorPanel_setupNodeMenu() {
+    // Set the pseudo classes
+    for (let name of ["hover", "active", "focus"]) {
+      let menu = this.panelDoc.getElementById("node-menu-pseudo-" + name);
+
+      if (this.selection.isElementNode()) {
+        let checked = DOMUtils.hasPseudoClassLock(this.selection.node, ":" + name);
+        menu.setAttribute("checked", checked);
+        menu.removeAttribute("disabled");
+      } else {
+        menu.setAttribute("disabled", "true");
+      }
+    }
+
+    // Disable delete item if needed
+    let deleteNode = this.panelDoc.getElementById("node-menu-delete");
+    if (this.selection.isRoot() || this.selection.isDocumentTypeNode()) {
+      deleteNode.setAttribute("disabled", "true");
+    } else {
+      deleteNode.removeAttribute("disabled");
+    }
+
+    // Disable / enable "Copy Unique Selector", "Copy inner HTML" &
+    // "Copy outer HTML" as appropriate
+    let unique = this.panelDoc.getElementById("node-menu-copyuniqueselector");
+    let copyInnerHTML = this.panelDoc.getElementById("node-menu-copyinner");
+    let copyOuterHTML = this.panelDoc.getElementById("node-menu-copyouter");
+    if (this.selection.isElementNode()) {
+      unique.removeAttribute("disabled");
+      copyInnerHTML.removeAttribute("disabled");
+      copyOuterHTML.removeAttribute("disabled");
+    } else {
+      unique.setAttribute("disabled", "true");
+      copyInnerHTML.setAttribute("disabled", "true");
+      copyOuterHTML.setAttribute("disabled", "true");
+    }
+  },
+
+  _resetNodeMenu: function InspectorPanel_resetNodeMenu() {
+    // Remove any extra items
+    while (this.lastNodemenuItem.nextSibling) {
+      let toDelete = this.lastNodemenuItem.nextSibling;
+      toDelete.parentNode.removeChild(toDelete);
+    }
+  },
+
+  _initMarkup: function InspectorPanel_initMarkup() {
+    let doc = this.panelDoc;
+
+    this._markupBox = doc.getElementById("markup-box");
+
+    // create tool iframe
+    this._markupFrame = doc.createElement("iframe");
+    this._markupFrame.setAttribute("flex", "1");
+    this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
+    this._markupFrame.setAttribute("context", "inspector-node-popup");
+
+    // This is needed to enable tooltips inside the iframe document.
+    this._boundMarkupFrameLoad = function InspectorPanel_initMarkupPanel_onload() {
+      this._markupFrame.contentWindow.focus();
+      this._onMarkupFrameLoad();
+    }.bind(this);
+    this._markupFrame.addEventListener("load", this._boundMarkupFrameLoad, true);
+
+    this._markupBox.setAttribute("hidden", true);
+    this._markupBox.appendChild(this._markupFrame);
+    this._markupFrame.setAttribute("src", "chrome://browser/content/devtools/markup-view.xhtml");
+  },
+
+  _onMarkupFrameLoad: function InspectorPanel__onMarkupFrameLoad() {
+    this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
+    delete this._boundMarkupFrameLoad;
+
+    this._markupBox.removeAttribute("hidden");
+
+    let controllerWindow = this._toolbox.doc.defaultView;
+    this.markup = new MarkupView(this, this._markupFrame, controllerWindow);
+
+    this.emit("markuploaded");
+  },
+
+  _destroyMarkup: function InspectorPanel__destroyMarkup() {
+    if (this._boundMarkupFrameLoad) {
+      this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
+      delete this._boundMarkupFrameLoad;
+    }
+
+    if (this.markup) {
+      this.markup.destroy();
+      delete this.markup;
+    }
+
+    if (this._markupFrame) {
+      this._markupFrame.parentNode.removeChild(this._markupFrame);
+      delete this._markupFrame;
+    }
+  },
+
+  /**
+   * Toggle a pseudo class.
+   */
+  togglePseudoClass: function InspectorPanel_togglePseudoClass(aPseudo) {
+    if (this.selection.isElementNode()) {
+      if (DOMUtils.hasPseudoClassLock(this.selection.node, aPseudo)) {
+        this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
+          DOMUtils.removePseudoClassLock(crumb.node, aPseudo);
+        });
+      } else {
+        let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
+        let node = this.selection.node;
+        do {
+          DOMUtils.addPseudoClassLock(node, aPseudo);
+          node = node.parentNode;
+        } while (hierarchical && node.parentNode)
+      }
+    }
+    this.selection.emit("pseudoclass");
+    this.breadcrumbs.scroll();
+  },
+
+  /**
+   * Clear any pseudo-class locks applied to the current hierarchy.
+   */
+  clearPseudoClasses: function InspectorPanel_clearPseudoClasses() {
+    this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
+      try {
+        DOMUtils.clearPseudoClassLocks(crumb.node);
+      } catch(e) {
+       // Ignore dead nodes after navigation.
+      }
+    });
+  },
+
+  /**
+   * Toggle the highlighter when ruleview is hovered.
+   */
+  toggleHighlighter: function InspectorPanel_toggleHighlighter(event)
+  {
+    if (event.type == "mouseover") {
+      this.highlighter.hide();
+    }
+    else if (event.type == "mouseout") {
+      this.highlighter.show();
+    }
+  },
+
+  /**
+   * Copy the innerHTML of the selected Node to the clipboard.
+   */
+  copyInnerHTML: function InspectorPanel_copyInnerHTML()
+  {
+    if (!this.selection.isNode()) {
+      return;
+    }
+    let toCopy = this.selection.node.innerHTML;
+    if (toCopy) {
+      clipboardHelper.copyString(toCopy);
+    }
+  },
+
+  /**
+   * Copy the outerHTML of the selected Node to the clipboard.
+   */
+  copyOuterHTML: function InspectorPanel_copyOuterHTML()
+  {
+    if (!this.selection.isNode()) {
+      return;
+    }
+    let toCopy = this.selection.node.outerHTML;
+    if (toCopy) {
+      clipboardHelper.copyString(toCopy);
+    }
+  },
+
+  /**
+   * Copy a unique selector of the selected Node to the clipboard.
+   */
+  copyUniqueSelector: function InspectorPanel_copyUniqueSelector()
+  {
+    if (!this.selection.isNode()) {
+      return;
+    }
+
+    let toCopy = CssLogic.findCssSelector(this.selection.node);
+    if (toCopy) {
+      clipboardHelper.copyString(toCopy);
+    }
+  },
+
+  /**
+   * Delete the selected node.
+   */
+  deleteNode: function IUI_deleteNode() {
+    if (!this.selection.isNode() ||
+         this.selection.isRoot()) {
+      return;
+    }
+
+    let toDelete = this.selection.node;
+
+    let parent = this.selection.node.parentNode;
+
+    // If the markup panel is active, use the markup panel to delete
+    // the node, making this an undoable action.
+    if (this.markup) {
+      this.markup.deleteNode(toDelete);
+    } else {
+      // remove the node from content
+      parent.removeChild(toDelete);
+    }
+  },
+
+  /**
+   * Schedule a low-priority change event for things like paint
+   * and resize.
+   */
+  scheduleLayoutChange: function Inspector_scheduleLayoutChange()
+  {
+    if (this._timer) {
+      return null;
+    }
+    this._timer = this.panelWin.setTimeout(function() {
+      this.emit("layout-change");
+      this._timer = null;
+    }.bind(this), LAYOUT_CHANGE_TIMER);
+  },
+
+  /**
+   * Cancel a pending low-priority change event if any is
+   * scheduled.
+   */
+  cancelLayoutChange: function Inspector_cancelLayoutChange()
+  {
+    if (this._timer) {
+      this.panelWin.clearTimeout(this._timer);
+      delete this._timer;
+    }
+  },
+
+}
+
+/////////////////////////////////////////////////////////////////////////
+//// Initializers
+
+loader.lazyGetter(InspectorPanel.prototype, "strings",
+  function () {
+    return Services.strings.createBundle(
+            "chrome://browser/locale/devtools/inspector.properties");
+  });
+
+loader.lazyGetter(this, "clipboardHelper", function() {
+  return Cc["@mozilla.org/widget/clipboardhelper;1"].
+    getService(Ci.nsIClipboardHelper);
+});
+
+
+loader.lazyGetter(this, "DOMUtils", function () {
+  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/selection.js
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {Cu} = require("chrome");
+let EventEmitter = require("devtools/shared/event-emitter");
+
+/**
+ * API
+ *
+ *   new Selection(node=null, track={attributes,detached});
+ *   destroy()
+ *   node (readonly)
+ *   setNode(node, origin="unknown")
+ *
+ * Helpers:
+ *
+ *   window
+ *   document
+ *   isRoot()
+ *   isNode()
+ *   isHTMLNode()
+ *
+ * Check the nature of the node:
+ *
+ *   isElementNode()
+ *   isAttributeNode()
+ *   isTextNode()
+ *   isCDATANode()
+ *   isEntityRefNode()
+ *   isEntityNode()
+ *   isProcessingInstructionNode()
+ *   isCommentNode()
+ *   isDocumentNode()
+ *   isDocumentTypeNode()
+ *   isDocumentFragmentNode()
+ *   isNotationNode()
+ *
+ * Events:
+ *   "new-node" when the inner node changed
+ *   "before-new-node" when the inner node is set to change
+ *   "attribute-changed" when an attribute is changed (only if tracked)
+ *   "detached" when the node (or one of its parents) is removed from the document (only if tracked)
+ *   "reparented" when the node (or one of its parents) is moved under a different node (only if tracked)
+ */
+
+/**
+ * A Selection object. Hold a reference to a node.
+ * Includes some helpers, fire some helpful events.
+ *
+ * @param node Inner node.
+ *    Can be null. Can be (un)set in the future via the "node" property;
+ * @param trackAttribute Tell if events should be fired when the attributes of
+ *    the ndoe change.
+ *
+ */
+function Selection(node=null, track={attributes:true,detached:true}) {
+  EventEmitter.decorate(this);
+  this._onMutations = this._onMutations.bind(this);
+  this.track = track;
+  this.setNode(node);
+}
+
+exports.Selection = Selection;
+
+Selection.prototype = {
+  _node: null,
+
+  _onMutations: function(mutations) {
+    let attributeChange = false;
+    let detached = false;
+    let parentNode = null;
+    for (let m of mutations) {
+      if (!attributeChange && m.type == "attributes") {
+        attributeChange = true;
+      }
+      if (m.type == "childList") {
+        if (!detached && !this.isConnected()) {
+          parentNode = m.target;
+          detached = true;
+        }
+      }
+    }
+
+    if (attributeChange)
+      this.emit("attribute-changed");
+    if (detached)
+      this.emit("detached", parentNode);
+  },
+
+  _attachEvents: function SN__attachEvents() {
+    if (!this.window || !this.isNode() || !this.track) {
+      return;
+    }
+
+    if (this.track.attributes) {
+      this._nodeObserver = new this.window.MutationObserver(this._onMutations);
+      this._nodeObserver.observe(this.node, {attributes: true});
+    }
+
+    if (this.track.detached) {
+      this._docObserver = new this.window.MutationObserver(this._onMutations);
+      this._docObserver.observe(this.document.documentElement, {childList: true, subtree: true});
+    }
+  },
+
+  _detachEvents: function SN__detachEvents() {
+    // `disconnect` fail if node's document has
+    // been deleted.
+    try {
+      if (this._nodeObserver)
+        this._nodeObserver.disconnect();
+    } catch(e) {}
+    try {
+      if (this._docObserver)
+        this._docObserver.disconnect();
+    } catch(e) {}
+  },
+
+  destroy: function SN_destroy() {
+    this._detachEvents();
+    this.setNode(null);
+  },
+
+  setNode: function SN_setNode(value, reason="unknown") {
+    this.reason = reason;
+    if (value !== this._node) {
+      this.emit("before-new-node", value, reason);
+      let previousNode = this._node;
+      this._detachEvents();
+      this._node = value;
+      this._attachEvents();
+      this.emit("new-node", previousNode, this.reason);
+    }
+  },
+
+  get node() {
+    return this._node;
+  },
+
+  get window() {
+    if (this.isNode()) {
+      return this.node.ownerDocument.defaultView;
+    }
+    return null;
+  },
+
+  get document() {
+    if (this.isNode()) {
+      return this.node.ownerDocument;
+    }
+    return null;
+  },
+
+  isRoot: function SN_isRootNode() {
+    return this.isNode() &&
+           this.isConnected() &&
+           this.node.ownerDocument.documentElement === this.node;
+  },
+
+  isNode: function SN_isNode() {
+    return (this.node &&
+            !Cu.isDeadWrapper(this.node) &&
+            this.node.ownerDocument &&
+            this.node.ownerDocument.defaultView &&
+            this.node instanceof this.node.ownerDocument.defaultView.Node);
+  },
+
+  isConnected: function SN_isConnected() {
+    try {
+      let doc = this.document;
+      return doc && doc.defaultView && doc.documentElement.contains(this.node);
+    } catch (e) {
+      // "can't access dead object" error
+      return false;
+    }
+  },
+
+  isHTMLNode: function SN_isHTMLNode() {
+    let xhtml_ns = "http://www.w3.org/1999/xhtml";
+    return this.isNode() && this.node.namespaceURI == xhtml_ns;
+  },
+
+  // Node type
+
+  isElementNode: function SN_isElementNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.ELEMENT_NODE;
+  },
+
+  isAttributeNode: function SN_isAttributeNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.ATTRIBUTE_NODE;
+  },
+
+  isTextNode: function SN_isTextNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.TEXT_NODE;
+  },
+
+  isCDATANode: function SN_isCDATANode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.CDATA_SECTION_NODE;
+  },
+
+  isEntityRefNode: function SN_isEntityRefNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.ENTITY_REFERENCE_NODE;
+  },
+
+  isEntityNode: function SN_isEntityNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.ENTITY_NODE;
+  },
+
+  isProcessingInstructionNode: function SN_isProcessingInstructionNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.PROCESSING_INSTRUCTION_NODE;
+  },
+
+  isCommentNode: function SN_isCommentNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.PROCESSING_INSTRUCTION_NODE;
+  },
+
+  isDocumentNode: function SN_isDocumentNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.DOCUMENT_NODE;
+  },
+
+  isDocumentTypeNode: function SN_isDocumentTypeNode() {
+    return this.isNode() && this.node.nodeType ==this.window. Node.DOCUMENT_TYPE_NODE;
+  },
+
+  isDocumentFragmentNode: function SN_isDocumentFragmentNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.DOCUMENT_FRAGMENT_NODE;
+  },
+
+  isNotationNode: function SN_isNotationNode() {
+    return this.isNode() && this.node.nodeType == this.window.Node.NOTATION_NODE;
+  },
+}
rename from browser/devtools/inspector/SelectorSearch.jsm
rename to browser/devtools/inspector/selector-search.js
--- a/browser/devtools/inspector/SelectorSearch.jsm
+++ b/browser/devtools/inspector/selector-search.js
@@ -1,21 +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/. */
 
 "use strict";
 
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+const {Cu} = require("chrome");
 
-XPCOMUtils.defineLazyModuleGetter(this, "AutocompletePopup",
-                                  "resource:///modules/devtools/AutocompletePopup.jsm");
-this.EXPORTED_SYMBOLS = ["SelectorSearch"];
+loader.lazyGetter(this, "AutocompletePopup", () => {
+  return Cu.import("resource:///modules/devtools/AutocompletePopup.jsm", {}).AutocompletePopup;
+});
 
 // Maximum number of selector suggestions shown in the panel.
 const MAX_SUGGESTIONS = 15;
 
 /**
  * Converts any input box on a page to a CSS selector search and suggestion box.
  *
  * @constructor
@@ -23,17 +21,17 @@ const MAX_SUGGESTIONS = 15;
  *        The content document which inspector is attached to.
  * @param nsiInputElement aInputNode
  *        The input element to which the panel will be attached and from where
  *        search input will be taken.
  * @param Function aCallback
  *        The method to callback when a search is available.
  *        This method is called with the matched node as the first argument.
  */
-this.SelectorSearch = function(aContentDocument, aInputNode, aCallback) {
+function SelectorSearch(aContentDocument, aInputNode, aCallback) {
   this.doc = aContentDocument;
   this.callback = aCallback;
   this.searchBox = aInputNode;
   this.panelDoc = this.searchBox.ownerDocument;
 
   // initialize variables.
   this._lastSearched = null;
   this._lastValidSearch = "";
@@ -61,17 +59,19 @@ this.SelectorSearch = function(aContentD
   };
   this.searchPopup = new AutocompletePopup(this.panelDoc, options);
 
   // event listeners.
   this.searchBox.addEventListener("command", this._onHTMLSearch, true);
   this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
 }
 
-this.SelectorSearch.prototype = {
+exports.SelectorSearch = SelectorSearch;
+
+SelectorSearch.prototype = {
 
   // The possible states of the query.
   States: {
     CLASS: "class",
     ID: "id",
     TAG: "tag",
   },
 
--- a/browser/devtools/inspector/test/browser_inspector_bug_840156_destroy_after_navigation.js
+++ b/browser/devtools/inspector/test/browser_inspector_bug_840156_destroy_after_navigation.js
@@ -1,21 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let temp = {};
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", temp);
-let Promise = temp.Promise;
-temp = {};
-Cu.import("resource:///modules/devtools/Toolbox.jsm", temp);
-let Toolbox = temp.Toolbox;
-temp = {};
-Cu.import("resource:///modules/devtools/Target.jsm", temp);
-let TargetFactory = temp.TargetFactory;
-temp = null;
+let Promise = devtools.require("sdk/core/promise");
+let Toolbox = devtools.Toolbox;
+let TargetFactory = devtools.TargetFactory;
 
 function test() {
   waitForExplicitFinish();
 
   const URL_1 = "data:text/plain;charset=UTF-8,abcde";
   const URL_2 = "data:text/plain;charset=UTF-8,12345";
 
   let target, toolbox;
--- a/browser/devtools/inspector/test/browser_inspector_initialization.js
+++ b/browser/devtools/inspector/test/browser_inspector_initialization.js
@@ -1,20 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 let doc;
 let salutation;
 
-let tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
-
 function createDocument()
 {
   doc.body.innerHTML = '<div id="first" style="{ margin: 10em; ' +
     'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA}">\n' +
     '<h1>Some header text</h1>\n' +
     '<p id="salutation" style="{font-size: 12pt}">hi.</p>\n' +
     '<p id="body" style="{font-size: 12pt}">I am a test-case. This text exists ' +
     'solely to provide some things to test the inspector initialization.</p>\n' +
--- a/browser/devtools/inspector/test/browser_inspector_pseudoclass_lock.js
+++ b/browser/devtools/inspector/test/browser_inspector_pseudoclass_lock.js
@@ -1,15 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
-
 let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 
 let doc;
 let parentDiv, div, div2;
 let inspector;
 let ruleview;
 
 let pseudo = ":hover";
--- a/browser/devtools/inspector/test/browser_inspector_sidebarstate.js
+++ b/browser/devtools/inspector/test/browser_inspector_sidebarstate.js
@@ -1,15 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
-
 let doc;
 let inspector;
 
 function createDocument()
 {
   doc.body.innerHTML = '<h1>Sidebar state test</h1>';
   doc.title = "Sidebar State Test";
 
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -3,18 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 let tempScope = {};
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
 let LayoutHelpers = tempScope.LayoutHelpers;
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
+
+let {devtools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
+let TargetFactory = devtools.TargetFactory;
+
 Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
 let console = tempScope.console;
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
 
 function openInspector(callback)
@@ -141,8 +143,9 @@ function focusSearchBoxUsingShortcut(pan
 
   let searchBox = panelWin.document.getElementById("inspector-searchbox");
   searchBox.addEventListener("focus", function onFocus() {
     searchBox.removeEventListener("focus", onFocus, false);
     callback && callback();
   }, false);
   EventUtils.synthesizeKey(name, modifiers);
 }
+
--- a/browser/devtools/inspector/test/helpers.js
+++ b/browser/devtools/inspector/test/helpers.js
@@ -19,17 +19,18 @@
 
 this.EXPORTED_SYMBOLS = [ 'helpers' ];
 var helpers = {};
 this.helpers = helpers;
 let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require;
 Components.utils.import("resource:///modules/devtools/gcli.jsm", {});
 
 let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
-let TargetFactory = (Cu.import("resource:///modules/devtools/Target.jsm", {})).TargetFactory;
+let {devtools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
+let TargetFactory = devtools.TargetFactory;
 
 let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
 let assert = { ok: ok, is: is, log: info };
 
 var util = require('util/util');
 
 var converters = require('gcli/converters');
 
--- a/browser/devtools/layoutview/test/browser_layoutview.js
+++ b/browser/devtools/layoutview/test/browser_layoutview.js
@@ -1,14 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let tempScope = {};
-Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
-let TargetFactory = tempScope.TargetFactory;
+let {devtools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
+let TargetFactory = devtools.TargetFactory;
 
 function test() {
   waitForExplicitFinish();
 
   Services.prefs.setBoolPref("devtools.layoutview.enabled", true);
   Services.prefs.setBoolPref("devtools.inspector.sidebarOpen", true);
 
   let doc;
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -4,19 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cu = Components.utils;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
-Cu.import("resource:///modules/devtools/CssLogic.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 
+let {CssLogic} = devtools.require("devtools/styleinspector/css-logic");
+
 function LayoutView(aInspector, aWindow)
 {
   this.inspector = aInspector;
 
   // <browser> is not always available (for Chrome targets for example)
   if (this.inspector.target.tab) {
     this.browser = aInspector.target.tab.linkedBrowser;
   }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/main.js
@@ -0,0 +1,229 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc, Ci, Cu} = require("chrome");
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+// Add a couple of globals that we use all over this package.
+let loaderOptions = require("@loader/options")
+
+loaderOptions.globals.loader = {
+  lazyGetter: XPCOMUtils.defineLazyGetter.bind(XPCOMUtils),
+  lazyImporter: XPCOMUtils.defineLazyModuleGetter.bind(XPCOMUtils)
+};
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource:///modules/devtools/gDevTools.jsm");
+
+Object.defineProperty(exports, "Toolbox", {
+  get: () => require("devtools/framework/toolbox").Toolbox
+});
+Object.defineProperty(exports, "TargetFactory", {
+  get: () => require("devtools/framework/target").TargetFactory
+});
+
+loader.lazyGetter(this, "osString", () => Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS);
+
+// Panels
+loader.lazyGetter(this, "InspectorPanel", function() require("devtools/inspector/inspector-panel").InspectorPanel);
+loader.lazyImporter(this, "WebConsolePanel", "resource:///modules/WebConsolePanel.jsm");
+loader.lazyImporter(this, "DebuggerPanel", "resource:///modules/devtools/DebuggerPanel.jsm");
+loader.lazyImporter(this, "StyleEditorPanel", "resource:///modules/devtools/StyleEditorPanel.jsm");
+loader.lazyImporter(this, "ProfilerPanel", "resource:///modules/devtools/ProfilerPanel.jsm");
+loader.lazyImporter(this, "NetMonitorPanel", "resource:///modules/devtools/NetMonitorPanel.jsm");
+
+// Strings
+const inspectorProps = "chrome://browser/locale/devtools/inspector.properties";
+const debuggerProps = "chrome://browser/locale/devtools/debugger.properties";
+const styleEditorProps = "chrome://browser/locale/devtools/styleeditor.properties";
+const webConsoleProps = "chrome://browser/locale/devtools/webconsole.properties";