Bug 650794 - Disable HTML panel and make it use registerTools API; r=msucan, gavin
☠☠ backed out by aa17cb6e4d1b ☠ ☠
authorRob Campbell <rcampbell@mozilla.com>
Fri, 23 Sep 2011 13:51:51 -0300
changeset 78710 121518f3df44e8837ef64e7536717a8fdeb36006
parent 78709 f1409901573ab504ffa35030198084679c0d8154
child 78711 b1c0b12a5f6512e335685f6dcf75694e5ed8d81c
child 78756 aa17cb6e4d1bae2453ecd78013bc0b18f5d4fa60
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmsucan, gavin
bugs650794
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 650794 - Disable HTML panel and make it use registerTools API; r=msucan, gavin
browser/base/content/browser.xul
browser/devtools/highlighter/InsideOutBox.jsm
browser/devtools/highlighter/Makefile.in
browser/devtools/highlighter/TreePanel.jsm
browser/devtools/highlighter/insideOutBox.js
browser/devtools/highlighter/inspector.js
browser/devtools/highlighter/test/Makefile.in
browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js
browser/devtools/highlighter/test/browser_inspector_editor.js
browser/devtools/highlighter/test/browser_inspector_iframeTest.js
browser/devtools/highlighter/test/browser_inspector_initialization.js
browser/devtools/highlighter/test/browser_inspector_tab_switch.js
browser/devtools/highlighter/test/browser_inspector_treePanel_click.js
browser/devtools/highlighter/test/browser_inspector_treePanel_output.js
browser/devtools/jar.mn
browser/devtools/scratchpad/Makefile.in
browser/locales/en-US/chrome/browser/inspector.properties
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -227,17 +227,16 @@
     <panel id="inspector-tree-panel"
            orient="vertical"
            hidden="true"
            ignorekeys="true"
            noautofocus="true"
            noautohide="true"
            titlebar="normal"
            close="true"
-           onpopuphiding="InspectorUI.closeInspectorUI();"
            label="&inspectPanelTitle.label;">
       <hbox id="tree-panel-resizer-box" align="end">
         <spacer flex="1" />
         <resizer dir="bottomend" />
       </hbox>
     </panel>
 
     <menupopup id="toolbar-context-menu"
rename from browser/devtools/highlighter/insideOutBox.js
rename to browser/devtools/highlighter/InsideOutBox.jsm
--- a/browser/devtools/highlighter/insideOutBox.js
+++ b/browser/devtools/highlighter/InsideOutBox.jsm
@@ -120,16 +120,19 @@ InsideOutBoxView = {
  *
  * Constructor
  * @param aView
  *        The view requiring the InsideOutBox.
  * @param aBox
  *        The box object containing the InsideOutBox. Required to add/remove
  *        children during box manipulation (toggling opened or closed).
  */
+
+var EXPORTED_SYMBOLS = ["InsideOutBox"];
+
 function InsideOutBox(aView, aBox)
 {
   this.view = aView;
   this.box = aBox;
 
   this.rootObject = null;
 
   this.rootObjectBox = null;
@@ -445,17 +448,17 @@ InsideOutBox.prototype =
       return null;
 
     if (aObject == aRootObject) {
       if (!this.rootObjectBox || this.rootObjectBox.repObject != aRootObject) {
         if (this.rootObjectBox) {
           try {
             this.box.removeChild(this.rootObjectBox);
           } catch (exc) {
-            InspectorUI._log("this.box.removeChild(this.rootObjectBox) FAILS " +
+            this.view._log("this.box.removeChild(this.rootObjectBox) FAILS " +
               this.box + " must not contain " + this.rootObjectBox);
           }
         }
 
         this.highlightedObjectBox = null;
         this.selectedObjectBox = null;
         this.rootObjectBox = this.view.createObjectBox(aObject, true);
         this.box.appendChild(this.rootObjectBox);
@@ -638,9 +641,23 @@ InsideOutBox.prototype =
   {
     let node = aNode;
     let tmpNode;
     while ((tmpNode = this.view.getParentObject(node)))
       node = tmpNode;
 
     return node;
   },
+
+  /**
+   * Clean up our mess.
+   */
+  destroy: function IOBox_destroy()
+  {
+    delete this.view;
+    delete this.box;
+    delete this.rootObject;
+    delete this.rootObjectBox;
+    delete this.selectedObjectBox;
+    delete this.highlightedObjectBox;
+    delete this.scrollIntoView;
+  }
 };
--- a/browser/devtools/highlighter/Makefile.in
+++ b/browser/devtools/highlighter/Makefile.in
@@ -41,15 +41,17 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
 	domplate.jsm \
+	InsideOutBox.jsm \
+	TreePanel.jsm \
 	$(NULL)
 
 ifdef ENABLE_TESTS
  	DIRS += test
 endif
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/TreePanel.jsm
@@ -0,0 +1,694 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla Tree Panel.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Campbell <rcampbell@mozilla.com> (original author)
+ *   Mihai Șucan <mihai.sucan@gmail.com>
+ *   Julian Viereck <jviereck@mozilla.com>
+ *   Paul Rouget <paul@mozilla.com>
+ *   Kyle Simpson <getify@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cu = Components.utils;
+
+Cu.import("resource:///modules/domplate.jsm");
+Cu.import("resource:///modules/InsideOutBox.jsm");
+Cu.import("resource:///modules/Services.jsm");
+
+var EXPORTED_SYMBOLS = ["TreePanel"];
+
+/**
+ * TreePanel
+ * A container for the Inspector's HTML Tree Panel widget constructor function.
+ * @param aContext nsIDOMWindow (xulwindow)
+ * @param aIUI global InspectorUI object
+ */
+function TreePanel(aContext, aIUI) {
+  this._init(aContext, aIUI);
+};
+
+TreePanel.prototype = {
+  showTextNodesWithWhitespace: false,
+  id: "treepanel", // DO NOT LOCALIZE
+
+  /**
+   * container.
+   * @returns xul:panel element
+   */
+  get container()
+  {
+    return this.document.getElementById("inspector-tree-panel");
+  },
+
+  /**
+   * Main TreePanel boot-strapping method. Initialize the TreePanel with the
+   * originating context and the InspectorUI global.
+   * @param aContext nsIDOMWindow (xulwindow)
+   * @param aIUI global InspectorUI object
+   */
+  _init: function TP__init(aContext, aIUI)
+  {
+    this.IUI = aIUI;
+    this.window = aContext;
+    this.document = this.window.document;
+
+    domplateUtils.setDOM(this.window);
+
+    let isOpen = this.isOpen.bind(this);
+
+    this.registrationObject = {
+      id: this.id,
+      label: this.IUI.strings.GetStringFromName("htmlPanel.label"),
+      tooltiptext: this.IUI.strings.GetStringFromName("htmlPanel.tooltiptext"),
+      accesskey: this.IUI.strings.GetStringFromName("htmlPanel.accesskey"),
+      context: this,
+      get isOpen() isOpen(),
+      show: this.open,
+      hide: this.close,
+      onSelect: this.select,
+      panel: this.container,
+      unregister: this.destroy,
+    };
+    this.editingEvents = {};
+
+    this._boundClose = this.close.bind(this);
+    this.container.addEventListener("popuphiding", this._boundClose, false);
+
+    // Register the HTML panel with the highlighter
+    this.IUI.registerTool(this.registrationObject);
+  },
+
+  /**
+   * Initialization function for the TreePanel.
+   */
+  initializeIFrame: function TP_initializeIFrame()
+  {
+    if (!this.initializingTreePanel || this.treeLoaded) {
+      return;
+    }
+    this.treeBrowserDocument = this.treeIFrame.contentDocument;
+    this.treePanelDiv = this.treeBrowserDocument.createElement("div");
+    this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
+    this.treePanelDiv.ownerPanel = this;
+    this.ioBox = new InsideOutBox(this, this.treePanelDiv);
+    this.ioBox.createObjectBox(this.IUI.win.document.documentElement);
+    this.treeLoaded = true;
+    this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false);
+    this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false);
+    delete this.initializingTreePanel;
+    Services.obs.notifyObservers(null,
+      this.window.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null);
+    if (this.IUI.selection)
+      this.select(this.IUI.selection, true);
+  },
+
+  /**
+   * Open the inspector's tree panel and initialize it.
+   */
+  open: function TP_open()
+  {
+    if (this.initializingTreePanel && !this.treeLoaded) {
+      return;
+    }
+
+    this.initializingTreePanel = true;
+    this.container.hidden = false;
+
+    this.treeIFrame = this.document.getElementById("inspector-tree-iframe");
+    if (!this.treeIFrame) {
+      let resizerBox = this.document.getElementById("tree-panel-resizer-box");
+      this.treeIFrame = this.document.createElement("iframe");
+      this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
+      this.treeIFrame.setAttribute("flex", "1");
+      this.treeIFrame.setAttribute("type", "content");
+      this.treeIFrame = this.container.insertBefore(this.treeIFrame, resizerBox);
+    }
+
+    let self = this;
+    this.container.addEventListener("popupshown", function treePanelShown() {
+      self.container.removeEventListener("popupshown",
+        treePanelShown, false);
+
+        self.treeIFrame.addEventListener("load",
+          function loadedInitializeTreePanel() {
+            self.treeIFrame.removeEventListener("load",
+              loadedInitializeTreePanel, true);
+            self.initializeIFrame();
+          }, true);
+
+      let src = self.treeIFrame.getAttribute("src");
+      if (src != "chrome://browser/content/inspector.html") {
+        self.treeIFrame.setAttribute("src",
+          "chrome://browser/content/inspector.html");
+      } else {
+        self.treeIFrame.contentWindow.location.reload();
+      }
+    }, false);
+
+    const panelWidthRatio = 7 / 8;
+    const panelHeightRatio = 1 / 5;
+
+    let width = parseInt(this.IUI.win.outerWidth * panelWidthRatio);
+    let height = parseInt(this.IUI.win.outerHeight * panelHeightRatio);
+    let y = Math.min(this.document.defaultView.screen.availHeight - height,
+      this.IUI.win.innerHeight);
+
+    this.container.openPopup(this.browser, "overlap", 0, 0,
+      false, false);
+
+    this.container.moveTo(80, y);
+    this.container.sizeTo(width, height);
+  },
+
+  /**
+   * Close the TreePanel.
+   */
+  close: function TP_close()
+  {
+    if (this.treePanelDiv) {
+      this.treePanelDiv.ownerPanel = null;
+      let parent = this.treePanelDiv.parentNode;
+      parent.removeChild(this.treePanelDiv);
+      delete this.treePanelDiv;
+      delete this.treeBrowserDocument;
+    }
+
+    this.treeLoaded = false;
+    this.container.hidePopup();
+  },
+
+  /**
+   * Is the TreePanel open?
+   * @returns boolean
+   */
+  isOpen: function TP_isOpen()
+  {
+    return this.treeLoaded && this.container.state == "open";
+  },
+
+  /**
+   * Create the ObjectBox for the given object.
+   * @param object nsIDOMNode
+   * @param isRoot boolean - Is this the root object?
+   * @returns InsideOutBox
+   */
+  createObjectBox: function TP_createObjectBox(object, isRoot)
+  {
+    let tag = domplateUtils.getNodeTag(object);
+    if (tag)
+      return tag.replace({object: object}, this.treeBrowserDocument);
+  },
+
+  getParentObject: function TP_getParentObject(node)
+  {
+    let parentNode = node ? node.parentNode : null;
+
+    if (!parentNode) {
+      // Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
+      // and Notation. top level windows have no parentNode
+      if (node && node == this.window.Node.DOCUMENT_NODE) {
+        // document type
+        if (node.defaultView) {
+          let embeddingFrame = node.defaultView.frameElement;
+          if (embeddingFrame)
+            return embeddingFrame.parentNode;
+        }
+      }
+      // a Document object without a parentNode or window
+      return null;  // top level has no parent
+    }
+
+    if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
+      if (parentNode.defaultView) {
+        return parentNode.defaultView.frameElement;
+      }
+      // parent is document element, but no window at defaultView.
+      return null;
+    }
+
+    if (!parentNode.localName)
+      return null;
+
+    return parentNode;
+  },
+
+  getChildObject: function TP_getChildObject(node, index, previousSibling)
+  {
+    if (!node)
+      return null;
+
+    if (node.contentDocument) {
+      // then the node is a frame
+      if (index == 0) {
+        return node.contentDocument.documentElement;  // the node's HTMLElement
+      }
+      return null;
+    }
+
+    if (node instanceof this.window.GetSVGDocument) {
+      let svgDocument = node.getSVGDocument();
+      if (svgDocument) {
+        // then the node is a frame
+        if (index == 0) {
+          return svgDocument.documentElement;  // the node's SVGElement
+        }
+        return null;
+      }
+    }
+
+    let child = null;
+    if (previousSibling)  // then we are walking
+      child = this.getNextSibling(previousSibling);
+    else
+      child = this.getFirstChild(node);
+
+    if (this.showTextNodesWithWhitespace)
+      return child;
+
+    for (; child; child = this.getNextSibling(child)) {
+      if (!domplateUtils.isWhitespaceText(child))
+        return child;
+    }
+
+    return null;  // we have no children worth showing.
+  },
+
+  getFirstChild: function TP_getFirstChild(node)
+  {
+    this.treeWalker = node.ownerDocument.createTreeWalker(node,
+      this.window.NodeFilter.SHOW_ALL, null, false);
+    return this.treeWalker.firstChild();
+  },
+
+  getNextSibling: function TP_getNextSibling(node)
+  {
+    let next = this.treeWalker.nextSibling();
+
+    if (!next)
+      delete this.treeWalker;
+
+    return next;
+  },
+
+  /////////////////////////////////////////////////////////////////////
+  // Event Handling
+
+  /**
+   * Handle click events in the html tree panel.
+   * @param aEvent
+   *        The mouse event.
+   */
+  onTreeClick: function TP_onTreeClick(aEvent)
+  {
+    let node;
+    let target = aEvent.target;
+    let hitTwisty = false;
+    if (this.hasClass(target, "twisty")) {
+      node = this.getRepObject(aEvent.target.nextSibling);
+      hitTwisty = true;
+    } else {
+      node = this.getRepObject(aEvent.target);
+    }
+
+    if (node) {
+      if (hitTwisty) {
+        this.ioBox.toggleObject(node);
+      } else {
+        if (this.IUI.inspecting) {
+          this.IUI.stopInspecting(true);
+        } else {
+          this.IUI.select(node, true, false);
+          this.IUI.highlighter.highlightNode(node);
+        }
+      }
+    }
+  },
+
+  /**
+   * Handle double-click events in the html tree panel.
+   * (double-clicking an attribute value allows it to be edited)
+   * @param aEvent
+   *        The mouse event.
+   */
+  onTreeDblClick: function TP_onTreeDblClick(aEvent)
+  {
+    // if already editing an attribute value, double-clicking elsewhere
+    // in the tree is the same as a click, which dismisses the editor
+    if (this.editingContext)
+      this.closeEditor();
+
+    let target = aEvent.target;
+
+    if (this.hasClass(target, "nodeValue")) {
+      let repObj = this.getRepObject(target);
+      let attrName = target.getAttribute("data-attributeName");
+      let attrVal = target.innerHTML;
+
+      this.editAttributeValue(target, repObj, attrName, attrVal);
+    }
+  },
+
+  /**
+   * Starts the editor for an attribute value.
+   * @param aAttrObj
+   *        The DOM object representing the attribute value in the HTML Tree
+   * @param aRepObj
+   *        The original DOM (target) object being inspected/edited
+   * @param aAttrName
+   *        The name of the attribute being edited
+   * @param aAttrVal
+   *        The current value of the attribute being edited
+   */
+  editAttributeValue:
+  function TP_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal)
+  {
+    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
+    let editorInput =
+      this.treeBrowserDocument.getElementById("attribute-editor-input");
+    let attrDims = aAttrObj.getBoundingClientRect();
+    // figure out actual viewable viewport dimensions (sans scrollbars)
+    let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
+    let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
+
+    // saves the editing context for use when the editor is saved/closed
+    this.editingContext = {
+      attrObj: aAttrObj,
+      repObj: aRepObj,
+      attrName: aAttrName
+    };
+
+    // highlight attribute-value node in tree while editing
+    this.addClass(aAttrObj, "editingAttributeValue");
+
+    // show the editor
+    this.addClass(editor, "editing");
+
+    // offset the editor below the attribute-value node being edited
+    let editorVeritcalOffset = 2;
+
+    // keep the editor comfortably within the bounds of the viewport
+    let editorViewportBoundary = 5;
+
+    // outer editor is sized based on the <input> box inside it
+    editorInput.style.width = Math.min(attrDims.width, viewportWidth -
+                                editorViewportBoundary) + "px";
+    editorInput.style.height = Math.min(attrDims.height, viewportHeight -
+                                editorViewportBoundary) + "px";
+    let editorDims = editor.getBoundingClientRect();
+
+    // calculate position for the editor according to the attribute node
+    let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
+                    // center the editor against the attribute value
+                    ((editorDims.width - attrDims.width) / 2);
+    let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY +
+                    attrDims.height + editorVeritcalOffset;
+
+    // but, make sure the editor stays within the visible viewport
+    editorLeft = Math.max(0, Math.min(
+                                      (this.treeIFrame.contentWindow.scrollX +
+                                          viewportWidth - editorDims.width),
+                                      editorLeft)
+                          );
+    editorTop = Math.max(0, Math.min(
+                                      (this.treeIFrame.contentWindow.scrollY +
+                                          viewportHeight - editorDims.height),
+                                      editorTop)
+                          );
+
+    // position the editor
+    editor.style.left = editorLeft + "px";
+    editor.style.top = editorTop + "px";
+
+    // set and select the text
+    editorInput.value = aAttrVal;
+    editorInput.select();
+
+    // listen for editor specific events
+    this.bindEditorEvent(editor, "click", function(aEvent) {
+      aEvent.stopPropagation();
+    });
+    this.bindEditorEvent(editor, "dblclick", function(aEvent) {
+      aEvent.stopPropagation();
+    });
+    this.bindEditorEvent(editor, "keypress",
+                          this.handleEditorKeypress.bind(this));
+
+    // event notification
+    Services.obs.notifyObservers(null, this.window.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED,
+                                  null);
+  },
+
+  /**
+   * Handle binding an event handler for the editor.
+   * (saves the callback for easier unbinding later)
+   * @param aEditor
+   *        The DOM object for the editor
+   * @param aEventName
+   *        The name of the event to listen for
+   * @param aEventCallback
+   *        The callback to bind to the event (and also to save for later
+   *          unbinding)
+   */
+  bindEditorEvent:
+  function TP_bindEditorEvent(aEditor, aEventName, aEventCallback)
+  {
+    this.editingEvents[aEventName] = aEventCallback;
+    aEditor.addEventListener(aEventName, aEventCallback, false);
+  },
+
+  /**
+   * Handle unbinding an event handler from the editor.
+   * (unbinds the previously bound and saved callback)
+   * @param aEditor
+   *        The DOM object for the editor
+   * @param aEventName
+   *        The name of the event being listened for
+   */
+  unbindEditorEvent: function TP_unbindEditorEvent(aEditor, aEventName)
+  {
+    aEditor.removeEventListener(aEventName, this.editingEvents[aEventName],
+                                  false);
+    this.editingEvents[aEventName] = null;
+  },
+
+  /**
+   * Handle keypress events in the editor.
+   * @param aEvent
+   *        The keyboard event.
+   */
+  handleEditorKeypress: function TP_handleEditorKeypress(aEvent)
+  {
+    if (aEvent.which == this.window.KeyEvent.DOM_VK_RETURN) {
+      this.saveEditor();
+    } else if (aEvent.keyCode == this.window.KeyEvent.DOM_VK_ESCAPE) {
+      this.closeEditor();
+    }
+  },
+
+  /**
+   * Close the editor and cleanup.
+   */
+  closeEditor: function TP_closeEditor()
+  {
+    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
+    let editorInput =
+      this.treeBrowserDocument.getElementById("attribute-editor-input");
+
+    // remove highlight from attribute-value node in tree
+    this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
+
+    // hide editor
+    this.removeClass(editor, "editing");
+
+    // stop listening for editor specific events
+    this.unbindEditorEvent(editor, "click");
+    this.unbindEditorEvent(editor, "dblclick");
+    this.unbindEditorEvent(editor, "keypress");
+
+    // clean up after the editor
+    editorInput.value = "";
+    editorInput.blur();
+    this.editingContext = null;
+    this.editingEvents = {};
+
+    // event notification
+    Services.obs.notifyObservers(null, this.window.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
+                                  null);
+  },
+
+  /**
+   * Commit the edits made in the editor, then close it.
+   */
+  saveEditor: function TP_saveEditor()
+  {
+    let editorInput =
+      this.treeBrowserDocument.getElementById("attribute-editor-input");
+
+    // set the new attribute value on the original target DOM element
+    this.editingContext.repObj.setAttribute(this.editingContext.attrName,
+                                              editorInput.value);
+
+    // update the HTML tree attribute value
+    this.editingContext.attrObj.innerHTML = editorInput.value;
+
+    this.IUI.isDirty = true;
+
+    // event notification
+    Services.obs.notifyObservers(null, this.window.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,
+                                  null);
+
+    this.closeEditor();
+  },
+
+  /**
+   * Simple tree select method.
+   * @param aNode the DOM node in the content document to select.
+   * @param aScroll boolean scroll to the visible node?
+   */
+  select: function TP_select(aNode, aScroll)
+  {
+    if (this.ioBox)
+      this.ioBox.select(aNode, true, true, aScroll);
+  },
+
+  ///////////////////////////////////////////////////////////////////////////
+  //// Utility functions
+
+  /**
+   * Does the given object have a class attribute?
+   * @param aNode
+   *        the DOM node.
+   * @param aClass
+   *        The class string.
+   * @returns boolean
+   */
+  hasClass: function TP_hasClass(aNode, aClass)
+  {
+    if (!(aNode instanceof this.window.Element))
+      return false;
+    return aNode.classList.contains(aClass);
+  },
+
+  /**
+   * Add the class name to the given object.
+   * @param aNode
+   *        the DOM node.
+   * @param aClass
+   *        The class string.
+   */
+  addClass: function TP_addClass(aNode, aClass)
+  {
+    if (aNode instanceof this.window.Element)
+      aNode.classList.add(aClass);
+  },
+
+  /**
+   * Remove the class name from the given object
+   * @param aNode
+   *        the DOM node.
+   * @param aClass
+   *        The class string.
+   */
+  removeClass: function TP_removeClass(aNode, aClass)
+  {
+    if (aNode instanceof this.window.Element)
+      aNode.classList.remove(aClass);
+  },
+
+  /**
+   * Get the "repObject" from the HTML panel's domplate-constructed DOM node.
+   * In this system, a "repObject" is the Object being Represented by the box
+   * object. It is the "real" object that we're building our facade around.
+   *
+   * @param element
+   *        The element in the HTML panel the user clicked.
+   * @returns either a real node or null
+   */
+  getRepObject: function TP_getRepObject(element)
+  {
+    let target = null;
+    for (let child = element; child; child = child.parentNode) {
+      if (this.hasClass(child, "repTarget"))
+        target = child;
+
+      if (child.repObject) {
+        if (!target && this.hasClass(child.repObject, "repIgnore"))
+          break;
+        else
+          return child.repObject;
+      }
+    }
+    return null;
+  },
+
+  /**
+   * Destructor function. Cleanup.
+   */
+  destroy: function TP_destroy()
+  {
+    if (this.isOpen()) {
+      this.close();
+    }
+
+    domplateUtils.setDOM(null);
+
+    delete this.treeWalker;
+
+    if (this.treePanelDiv) {
+      this.treePanelDiv.ownerPanel = null;
+      let parent = this.treePanelDiv.parentNode;
+      parent.removeChild(this.treePanelDiv);
+      delete this.treePanelDiv;
+      delete this.treeBrowserDocument;
+    }
+
+    if (this.treeIFrame) {
+      this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false);
+      this.treeIFrame.removeEventListener("click", this.onTreeClick, false);
+      let parent = this.treeIFrame.parentNode;
+      parent.removeChild(this.treeIFrame);
+      delete this.treeIFrame;
+    }
+
+    if (this.ioBox) {
+      this.ioBox.destroy();
+      delete this.ioBox;
+    }
+
+    this.container.removeEventListener("popuphiding", this._boundClose, false);
+    delete this._boundClose;
+  }
+};
+
--- a/browser/devtools/highlighter/inspector.js
+++ b/browser/devtools/highlighter/inspector.js
@@ -21,35 +21,33 @@
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Rob Campbell <rcampbell@mozilla.com> (original author)
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *   Julian Viereck <jviereck@mozilla.com>
  *   Paul Rouget <paul@mozilla.com>
- *   Kyle Simpson <ksimpson@mozilla.com> 
+ *   Kyle Simpson <ksimpson@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #endif
 
-#include insideOutBox.js
-
 const INSPECTOR_INVISIBLE_ELEMENTS = {
   "head": true,
   "base": true,
   "basefont": true,
   "isindex": true,
   "link": true,
   "meta": true,
   "script": true,
@@ -67,16 +65,19 @@ const INSPECTOR_NOTIFICATIONS = {
 
   // Fires once the Inspector completes the initialization and opens up on
   // screen.
   OPENED: "inspector-opened",
 
   // Fires once the Inspector is closed.
   CLOSED: "inspector-closed",
 
+  // Fires when the Tree Panel is opened and initialized.
+  TREEPANELREADY: "inspector-treepanel-ready",
+
   // Event notifications for the attribute-value editor
   EDITOR_OPENED: "inspector-editor-opened",
   EDITOR_CLOSED: "inspector-editor-closed",
   EDITOR_SAVED: "inspector-editor-saved",
 };
 
 ///////////////////////////////////////////////////////////////////////////
 //// Highlighter
@@ -549,34 +550,33 @@ Highlighter.prototype = {
 
 /**
  * Main controller class for the Inspector.
  */
 var InspectorUI = {
   browser: null,
   tools: {},
   toolEvents: {},
-  showTextNodesWithWhitespace: false,
   inspecting: false,
-  treeLoaded: false,
+  treePanelEnabled: true,
   get enabled()
   {
     return gPrefService.getBoolPref("devtools.inspector.enabled");
   },
   isDirty: false,
 
   /**
    * Toggle the inspector interface elements on or off.
    *
    * @param aEvent
    *        The event that requested the UI change. Toolbar button or menu.
    */
   toggleInspectorUI: function IUI_toggleInspectorUI(aEvent)
   {
-    if (this.isTreePanelOpen) {
+    if (this.isInspectorOpen) {
       this.closeInspectorUI();
     } else {
       this.openInspectorUI();
     }
   },
 
   /**
    * Toggle the status of the inspector, starting or stopping it. Invoked
@@ -587,216 +587,46 @@ var InspectorUI = {
     if (this.inspecting) {
       this.stopInspecting();
     } else {
       this.startInspecting();
     }
   },
 
   /**
-   * Is the tree panel open?
+   * Is the inspector UI open? Simply check if the toolbar is visible or not.
    *
    * @returns boolean
    */
-  get isTreePanelOpen()
+  get isInspectorOpen()
   {
-    return this.treePanel && this.treePanel.state == "open";
+    return this.toolbar && !this.toolbar.hidden;
   },
 
   /**
    * Return the default selection element for the inspected document.
    */
   get defaultSelection()
   {
     let doc = this.win.document;
     return doc.documentElement ? doc.documentElement.lastElementChild : null;
   },
 
-  initializeTreePanel: function IUI_initializeTreePanel()
-  {
-    this.treeBrowserDocument = this.treeIFrame.contentDocument;
-    this.treePanelDiv = this.treeBrowserDocument.createElement("div");
-    this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
-    this.treePanelDiv.ownerPanel = this;
-    this.ioBox = new InsideOutBox(this, this.treePanelDiv);
-    this.ioBox.createObjectBox(this.win.document.documentElement);
-    this.treeLoaded = true;
-    this.editingContext = null;
-    this.editingEvents = {};
-
-    // initialize the highlighter
-    this.initializeHighlighter();
-  },
-
-  /**
-   * Open the inspector's tree panel and initialize it.
-   */
-  openTreePanel: function IUI_openTreePanel()
-  {
-    if (!this.treePanel) {
-      this.treePanel = document.getElementById("inspector-tree-panel");
-      this.treePanel.hidden = false;
-    }
-
-    this.treeIFrame = document.getElementById("inspector-tree-iframe");
-    if (!this.treeIFrame) {
-      let resizerBox = document.getElementById("tree-panel-resizer-box");
-      this.treeIFrame = document.createElement("iframe");
-      this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
-      this.treeIFrame.setAttribute("flex", "1");
-      this.treeIFrame.setAttribute("type", "content");
-      this.treeIFrame.setAttribute("onclick", "InspectorUI.onTreeClick(event)");
-      this.treeIFrame.setAttribute("ondblclick", "InspectorUI.onTreeDblClick(event);");
-      this.treeIFrame = this.treePanel.insertBefore(this.treeIFrame, resizerBox);
-    }
-
-    this.treePanel.addEventListener("popupshown", function treePanelShown() {
-      InspectorUI.treePanel.removeEventListener("popupshown",
-        treePanelShown, false);
-
-      InspectorUI.treeIFrame.addEventListener("load",
-        function loadedInitializeTreePanel() {
-          InspectorUI.treeIFrame.removeEventListener("load",
-            loadedInitializeTreePanel, true);
-          InspectorUI.initializeTreePanel();
-        }, true);
-
-      let src = InspectorUI.treeIFrame.getAttribute("src");
-      if (src != "chrome://browser/content/inspector.html") {
-        InspectorUI.treeIFrame.setAttribute("src",
-          "chrome://browser/content/inspector.html");
-      } else {
-        InspectorUI.treeIFrame.contentWindow.location.reload();
-      }
-
-    }, false);
-
-    const panelWidthRatio = 7 / 8;
-    const panelHeightRatio = 1 / 5;
-
-    let width = parseInt(this.win.outerWidth * panelWidthRatio);
-    let height = parseInt(this.win.outerHeight * panelHeightRatio);
-    let y = Math.min(window.screen.availHeight - height, this.win.innerHeight);
-
-    this.treePanel.openPopup(this.browser, "overlap", 0, 0,
-      false, false);
-
-    this.treePanel.moveTo(80, y);
-    this.treePanel.sizeTo(width, height);
-  },
-
-  createObjectBox: function IUI_createObjectBox(object, isRoot)
-  {
-    let tag = this.domplateUtils.getNodeTag(object);
-    if (tag)
-      return tag.replace({object: object}, this.treeBrowserDocument);
-  },
-
-  getParentObject: function IUI_getParentObject(node)
-  {
-    let parentNode = node ? node.parentNode : null;
-
-    if (!parentNode) {
-      // Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
-      // and Notation. top level windows have no parentNode
-      if (node && node == Node.DOCUMENT_NODE) {
-        // document type
-        if (node.defaultView) {
-          let embeddingFrame = node.defaultView.frameElement;
-          if (embeddingFrame)
-            return embeddingFrame.parentNode;
-        }
-      }
-      // a Document object without a parentNode or window
-      return null;  // top level has no parent
-    }
-
-    if (parentNode.nodeType == Node.DOCUMENT_NODE) {
-      if (parentNode.defaultView) {
-        return parentNode.defaultView.frameElement;
-      }
-      // parent is document element, but no window at defaultView.
-      return null;
-    }
-    if (!parentNode.localName) {
-      return null;
-    }
-    return parentNode;
-  },
-
-  getChildObject: function IUI_getChildObject(node, index, previousSibling)
-  {
-    if (!node)
-      return null;
-
-    if (node.contentDocument) {
-      // then the node is a frame
-      if (index == 0) {
-        return node.contentDocument.documentElement;  // the node's HTMLElement
-      }
-      return null;
-    }
-
-    if (node instanceof GetSVGDocument) {
-      let svgDocument = node.getSVGDocument();
-      if (svgDocument) {
-        // then the node is a frame
-        if (index == 0) {
-          return svgDocument.documentElement;  // the node's SVGElement
-        }
-        return null;
-      }
-    }
-
-    let child = null;
-    if (previousSibling)  // then we are walking
-      child = this.getNextSibling(previousSibling);
-    else
-      child = this.getFirstChild(node);
-
-    if (this.showTextNodesWithWhitespace)
-      return child;
-
-    for (; child; child = this.getNextSibling(child)) {
-      if (!this.domplateUtils.isWhitespaceText(child))
-        return child;
-    }
-
-    return null;  // we have no children worth showing.
-  },
-
-  getFirstChild: function IUI_getFirstChild(node)
-  {
-    this.treeWalker = node.ownerDocument.createTreeWalker(node,
-      NodeFilter.SHOW_ALL, null, false);
-    return this.treeWalker.firstChild();
-  },
-
-  getNextSibling: function IUI_getNextSibling(node)
-  {
-    let next = this.treeWalker.nextSibling();
-
-    if (!next)
-      delete this.treeWalker;
-
-    return next;
-  },
-
   /**
    * Open inspector UI and HTML tree. Add listeners for document scrolling,
    * resize, tabContainer.TabSelect and others. If a node is provided, then
    * start inspecting it.
    *
    * @param [optional] aNode
    *        The node to inspect.
    */
   openInspectorUI: function IUI_openInspectorUI(aNode)
   {
     // InspectorUI is already up and running. Lock a node if asked (via context).
-    if (this.treeLoaded && this.highlighter && aNode) {
+    if (this.highlighter && aNode) {
       this.inspectNode(aNode);
       this.stopInspecting();
       return;
     }
     // Observer used to inspect the specified element from content after the
     // inspector UI has been opened.
     function inspectObserver(aElement) {
       Services.obs.removeObserver(boundInspectObserver,
@@ -816,27 +646,30 @@ var InspectorUI = {
     // Start initialization.
     this.browser = gBrowser.selectedBrowser;
     this.win = this.browser.contentWindow;
     this.winID = this.getWindowID(this.win);
     this.toolbar = document.getElementById("inspector-toolbar");
 
     this.initTools();
 
-    if (!this.domplate) {
-      Cu.import("resource:///modules/domplate.jsm", this);
-      this.domplateUtils.setDOM(window);
+    if (!this.TreePanel && this.treePanelEnabled) {
+      Cu.import("resource:///modules/TreePanel.jsm", this);
+      this.treePanel = new this.TreePanel(window, this);
     }
 
-    this.openTreePanel();
+    this.toolbar.hidden = false;
+    this.inspectMenuitem.setAttribute("checked", true);
 
-    this.toolbar.hidden = false;
-    this.inspectCmd.setAttribute("checked", true);
+    this.isDirty = false;
 
     gBrowser.addProgressListener(InspectorProgressListener);
+
+    // initialize the highlighter
+    this.initializeHighlighter();
   },
 
   /**
    * Register and initialize any included tools.
    */
   initTools: function IUI_initTools()
   {
     // Style inspector
@@ -880,21 +713,23 @@ var InspectorUI = {
       gBrowser.tabContainer.addEventListener("TabSelect", this, false);
 
     // Has this windowID been inspected before?
     if (InspectorStore.hasID(this.winID)) {
       let selectedNode = InspectorStore.getValue(this.winID, "selectedNode");
       if (selectedNode) {
         this.inspectNode(selectedNode);
       }
+      this.isDirty = InspectorStore.getValue(this.winID, "isDirty");
     } else {
       // First time inspecting, set state to no selection + live inspection.
       InspectorStore.addStore(this.winID);
       InspectorStore.setValue(this.winID, "selectedNode", null);
       InspectorStore.setValue(this.winID, "inspecting", true);
+      InspectorStore.setValue(this.winID, "isDirty", this.isDirty);
       this.win.addEventListener("pagehide", this, true);
     }
   },
 
   /**
    * Close inspector UI and associated panels. Unhighlight and stop inspecting.
    * Remove event listeners for document scrolling, resize,
    * tabContainer.TabSelect and others.
@@ -903,18 +738,18 @@ var InspectorUI = {
    *        Tells if you want the store associated to the current tab/window to
    *        be cleared or not. Set this to true to not clear the store, or false
    *        otherwise.
    */
   closeInspectorUI: function IUI_closeInspectorUI(aKeepStore)
   {
     // if currently editing an attribute value, closing the
     // highlighter/HTML panel dismisses the editor
-    if (this.editingContext)
-      this.closeEditor();
+    if (this.treePanel && this.treePanel.editingContext)
+      this.treePanel.closeEditor();
 
     if (this.closing || !this.win || !this.browser) {
       return;
     }
 
     this.closing = true;
     this.toolbar.hidden = true;
 
@@ -925,16 +760,17 @@ var InspectorUI = {
       this.win.removeEventListener("pagehide", this, true);
     } else {
       // Update the store before closing.
       if (this.selection) {
         InspectorStore.setValue(this.winID, "selectedNode",
           this.selection);
       }
       InspectorStore.setValue(this.winID, "inspecting", this.inspecting);
+      InspectorStore.setValue(this.winID, "isDirty", this.isDirty);
     }
 
     if (InspectorStore.isEmpty()) {
       gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
     }
 
     this.stopInspecting();
 
@@ -943,68 +779,42 @@ var InspectorUI = {
       this.unregisterTool(aTool);
     }.bind(this));
 
     if (this.highlighter) {
       this.highlighter.destroy();
       this.highlighter = null;
     }
 
-    if (this.treePanelDiv) {
-      this.treePanelDiv.ownerPanel = null;
-      let parent = this.treePanelDiv.parentNode;
-      parent.removeChild(this.treePanelDiv);
-      delete this.treePanelDiv;
-      delete this.treeBrowserDocument;
-    }
-
-    if (this.treeIFrame) {
-      let parent = this.treeIFrame.parentNode;
-      parent.removeChild(this.treeIFrame);
-      delete this.treeIFrame;
-    }
-    delete this.ioBox;
-
-    if (this.domplate) {
-      this.domplateUtils.setDOM(null);
-      delete this.domplate;
-      delete this.HTMLTemplates;
-      delete this.domplateUtils;
-    }
-
-    this.inspectCmd.setAttribute("checked", false);
+    this.inspectMenuitem.setAttribute("checked", false);
     this.browser = this.win = null; // null out references to browser and window
     this.winID = null;
     this.selection = null;
-    this.treeLoaded = false;
-
-    this.treePanel.addEventListener("popuphidden", function treePanelHidden() {
-      this.removeEventListener("popuphidden", treePanelHidden, false);
+    this.closing = false;
+    this.isDirty = false;
 
-      InspectorUI.closing = false;
-      Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null);
-    }, false);
-
-    this.treePanel.hidePopup();
     delete this.treePanel;
     delete this.stylePanel;
+    delete this.toolbar;
+    delete this.TreePanel;
+    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null);
   },
 
   /**
    * Begin inspecting webpage, attach page event listeners, activate
    * highlighter event listeners.
    */
   startInspecting: function IUI_startInspecting()
   {
     // if currently editing an attribute value, starting
     // "live inspection" mode closes the editor
-    if (this.editingContext)
-      this.closeEditor();
+    if (this.treePanel && this.treePanel.editingContext)
+      this.treePanel.closeEditor();
 
-    document.getElementById("inspector-inspect-toolbutton").checked = true;
+    this.inspectToolbutton.checked = true;
     this.attachPageListeners();
     this.inspecting = true;
     this.toolsDim(true);
     this.highlighter.veilContainer.removeAttribute("locked");
   },
 
   /**
    * Stop inspecting webpage, detach page listeners, disable highlighter
@@ -1013,17 +823,17 @@ var InspectorUI = {
    *        Prevent scroll in the HTML tree?
    */
   stopInspecting: function IUI_stopInspecting(aPreventScroll)
   {
     if (!this.inspecting) {
       return;
     }
 
-    document.getElementById("inspector-inspect-toolbutton").checked = false;
+    this.inspectToolbutton.checked = false;
     this.detachPageListeners();
     this.inspecting = false;
     this.toolsDim(false);
     if (this.highlighter.node) {
       this.select(this.highlighter.node, true, true, !aPreventScroll);
     } else {
       this.select(null, true, true);
     }
@@ -1031,38 +841,37 @@ var InspectorUI = {
   },
 
   /**
    * Select an object in the tree view.
    * @param aNode
    *        node to inspect
    * @param forceUpdate
    *        force an update?
-   * @param aScroll
-   *        force scroll?
+   * @param aScroll boolean
+   *        scroll the tree panel?
    */
   select: function IUI_select(aNode, forceUpdate, aScroll)
   {
     // if currently editing an attribute value, using the
     // highlighter dismisses the editor
-    if (this.editingContext)
-      this.closeEditor();
+    if (this.treePanel && this.treePanel.editingContext)
+      this.treePanel.closeEditor();
 
     if (!aNode)
       aNode = this.defaultSelection;
 
     if (forceUpdate || aNode != this.selection) {
       this.selection = aNode;
       if (!this.inspecting) {
         this.highlighter.highlightNode(this.selection);
       }
-      this.ioBox.select(this.selection, true, true, aScroll);
     }
 
-    this.toolsSelect();
+    this.toolsSelect(aScroll);
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
 
   highlighterReady: function IUI_highlighterReady()
   {
     // Setup the InspectorStore or restore state
@@ -1088,17 +897,17 @@ var InspectorUI = {
   {
     let winID = null;
     let win = null;
     let inspectorClosed = false;
 
     switch (event.type) {
       case "TabSelect":
         winID = this.getWindowID(gBrowser.selectedBrowser.contentWindow);
-        if (this.isTreePanelOpen && winID != this.winID) {
+        if (this.isInspectorOpen && winID != this.winID) {
           this.closeInspectorUI(true);
           inspectorClosed = true;
         }
 
         if (winID && InspectorStore.hasID(winID)) {
           if (inspectorClosed && this.closing) {
             Services.obs.addObserver(function reopenInspectorForTab() {
               Services.obs.removeObserver(reopenInspectorForTab,
@@ -1144,275 +953,16 @@ var InspectorUI = {
             }
             break;
         }
         break;
     }
   },
 
   /**
-   * Handle click events in the html tree panel.
-   * @param aEvent
-   *        The mouse event.
-   */
-  onTreeClick: function IUI_onTreeClick(aEvent)
-  {
-    // if currently editing an attribute value, clicking outside
-    // the editor dismisses the editor
-    if (this.editingContext) {
-      this.closeEditor();
-
-      // clicking outside the editor ONLY closes the editor
-      // so, cancel the rest of the processing of this event
-      aEvent.preventDefault();
-      return;
-    }
-
-    let node;
-    let target = aEvent.target;
-    let hitTwisty = false;
-    if (this.hasClass(target, "twisty")) {
-      node = this.getRepObject(aEvent.target.nextSibling);
-      hitTwisty = true;
-    } else {
-      node = this.getRepObject(aEvent.target);
-    }
-
-    if (node) {
-      if (hitTwisty) {
-        this.ioBox.toggleObject(node);
-      } else {
-        if (this.inspecting) {
-          this.toolsSelect();
-          this.stopInspecting(true);
-        } else {
-          this.select(node, true, false);
-          this.highlighter.highlightNode(node);
-          this.toolsSelect();
-        }
-      }
-    }
-  },
-
-  /**
-   * Handle double-click events in the html tree panel.
-   * (double-clicking an attribute value allows it to be edited)
-   * @param aEvent
-   *        The mouse event.
-   */
-  onTreeDblClick: function IUI_onTreeDblClick(aEvent)
-  {
-    // if already editing an attribute value, double-clicking elsewhere
-    // in the tree is the same as a click, which dismisses the editor
-    if (this.editingContext)
-      this.closeEditor();
-
-    let target = aEvent.target;
-    if (this.hasClass(target, "nodeValue")) {
-      let repObj = this.getRepObject(target);
-      let attrName = target.getAttribute("data-attributeName");
-      let attrVal = target.innerHTML;
-
-      this.editAttributeValue(target, repObj, attrName, attrVal);
-    }
-  },
-
-  /**
-   * Starts the editor for an attribute value.
-   * @param aAttrObj
-   *        The DOM object representing the attribute value in the HTML Tree
-   * @param aRepObj
-   *        The original DOM (target) object being inspected/edited
-   * @param aAttrName
-   *        The name of the attribute being edited
-   * @param aAttrVal
-   *        The current value of the attribute being edited
-   */
-  editAttributeValue: 
-  function IUI_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal)
-  {
-    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
-    let editorInput = 
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-    let attrDims = aAttrObj.getBoundingClientRect();
-    // figure out actual viewable viewport dimensions (sans scrollbars)
-    let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
-    let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
-
-    // saves the editing context for use when the editor is saved/closed
-    this.editingContext = {
-      attrObj: aAttrObj,
-      repObj: aRepObj,
-      attrName: aAttrName
-    };
-
-    // highlight attribute-value node in tree while editing
-    this.addClass(aAttrObj, "editingAttributeValue");
-
-    // show the editor
-    this.addClass(editor, "editing");
-
-    // offset the editor below the attribute-value node being edited
-    let editorVeritcalOffset = 2;
-
-    // keep the editor comfortably within the bounds of the viewport
-    let editorViewportBoundary = 5;
-
-    // outer editor is sized based on the <input> box inside it
-    editorInput.style.width = Math.min(attrDims.width, viewportWidth - 
-                                editorViewportBoundary) + "px";
-    editorInput.style.height = Math.min(attrDims.height, viewportHeight - 
-                                editorViewportBoundary) + "px";
-    let editorDims = editor.getBoundingClientRect();
-
-    // calculate position for the editor according to the attribute node
-    let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
-                    // center the editor against the attribute value    
-                    ((editorDims.width - attrDims.width) / 2); 
-    let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY + 
-                    attrDims.height + editorVeritcalOffset;
-
-    // but, make sure the editor stays within the visible viewport
-    editorLeft = Math.max(0, Math.min(
-                                      (this.treeIFrame.contentWindow.scrollX + 
-                                          viewportWidth - editorDims.width),
-                                      editorLeft)
-                          );
-    editorTop = Math.max(0, Math.min(
-                                      (this.treeIFrame.contentWindow.scrollY + 
-                                          viewportHeight - editorDims.height),
-                                      editorTop)
-                          );
-
-    // position the editor
-    editor.style.left = editorLeft + "px";
-    editor.style.top = editorTop + "px";
-
-    // set and select the text
-    editorInput.value = aAttrVal;
-    editorInput.select();
-
-    // listen for editor specific events
-    this.bindEditorEvent(editor, "click", function(aEvent) {
-      aEvent.stopPropagation();
-    });
-    this.bindEditorEvent(editor, "dblclick", function(aEvent) {
-      aEvent.stopPropagation();
-    });
-    this.bindEditorEvent(editor, "keypress", 
-                          this.handleEditorKeypress.bind(this));
-
-    // event notification    
-    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, 
-                                  null);
-  },
-
-  /**
-   * Handle binding an event handler for the editor.
-   * (saves the callback for easier unbinding later)   
-   * @param aEditor
-   *        The DOM object for the editor
-   * @param aEventName
-   *        The name of the event to listen for
-   * @param aEventCallback
-   *        The callback to bind to the event (and also to save for later 
-   *          unbinding)
-   */
-  bindEditorEvent: 
-  function IUI_bindEditorEvent(aEditor, aEventName, aEventCallback)
-  {
-    this.editingEvents[aEventName] = aEventCallback;
-    aEditor.addEventListener(aEventName, aEventCallback, false);
-  },
-
-  /**
-   * Handle unbinding an event handler from the editor.
-   * (unbinds the previously bound and saved callback)   
-   * @param aEditor
-   *        The DOM object for the editor
-   * @param aEventName
-   *        The name of the event being listened for
-   */
-  unbindEditorEvent: function IUI_unbindEditorEvent(aEditor, aEventName)
-  {
-    aEditor.removeEventListener(aEventName, this.editingEvents[aEventName], 
-                                  false);
-    this.editingEvents[aEventName] = null;
-  },
-
-  /**
-   * Handle keypress events in the editor.
-   * @param aEvent
-   *        The keyboard event.
-   */
-  handleEditorKeypress: function IUI_handleEditorKeypress(aEvent)
-  {
-    if (aEvent.which == KeyEvent.DOM_VK_RETURN) {
-      this.saveEditor();
-    } else if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) {
-      this.closeEditor();
-    }
-  },
-
-  /**
-   * Close the editor and cleanup.
-   */
-  closeEditor: function IUI_closeEditor()
-  {
-    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
-    let editorInput = 
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-
-    // remove highlight from attribute-value node in tree
-    this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
-
-    // hide editor
-    this.removeClass(editor, "editing");
-
-    // stop listening for editor specific events
-    this.unbindEditorEvent(editor, "click");
-    this.unbindEditorEvent(editor, "dblclick");
-    this.unbindEditorEvent(editor, "keypress");
-
-    // clean up after the editor
-    editorInput.value = "";
-    editorInput.blur();
-    this.editingContext = null;
-    this.editingEvents = {};
-
-    // event notification    
-    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, 
-                                  null);
-  },
-
-  /**
-   * Commit the edits made in the editor, then close it.
-   */
-  saveEditor: function IUI_saveEditor()
-  {
-    let editorInput = 
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-
-    // set the new attribute value on the original target DOM element
-    this.editingContext.repObj.setAttribute(this.editingContext.attrName, 
-                                              editorInput.value);
-
-    // update the HTML tree attribute value
-    this.editingContext.attrObj.innerHTML = editorInput.value;
-
-    this.isDirty = true;
-
-    // event notification    
-    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, 
-                                  null);
-    
-    this.closeEditor();
-  },
-
-  /**
    * Attach event listeners to content window and child windows to enable
    * highlighting and click to stop inspection.
    */
   attachPageListeners: function IUI_attachPageListeners()
   {
     this.browser.addEventListener("keypress", this, true);
     this.highlighter.attachInspectListeners();
   },
@@ -1507,57 +1057,16 @@ var InspectorUI = {
 
     let borderTop = parseInt(style.getPropertyValue("border-top-width"));
     let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
 
     return [borderTop + paddingTop, borderLeft + paddingLeft];
   },
 
   /**
-   * Does the given object have a class attribute?
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   * @returns boolean
-   */
-  hasClass: function IUI_hasClass(aNode, aClass)
-  {
-    if (!(aNode instanceof Element))
-      return false;
-    return aNode.classList.contains(aClass);
-  },
-
-  /**
-   * Add the class name to the given object.
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   */
-  addClass: function IUI_addClass(aNode, aClass)
-  {
-    if (aNode instanceof Element)
-      aNode.classList.add(aClass);
-  },
-
-  /**
-   * Remove the class name from the given object
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   */
-  removeClass: function IUI_removeClass(aNode, aClass)
-  {
-    if (aNode instanceof Element)
-      aNode.classList.remove(aClass);
-  },
-
-  /**
    * Retrieve the unique ID of a window object.
    *
    * @param nsIDOMWindow aWindow
    * @returns integer ID
    */
   getWindowID: function IUI_getWindowID(aWindow)
   {
     if (!aWindow) {
@@ -1570,42 +1079,16 @@ var InspectorUI = {
       util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
         getInterface(Ci.nsIDOMWindowUtils);
     } catch (ex) { }
 
     return util.currentInnerWindowID;
   },
 
   /**
-   * Get the "repObject" from the HTML panel's domplate-constructed DOM node.
-   * In this system, a "repObject" is the Object being Represented by the box
-   * object. It is the "real" object that we're building our facade around.
-   *
-   * @param element
-   *        The element in the HTML panel the user clicked.
-   * @returns either a real node or null
-   */
-  getRepObject: function IUI_getRepObject(element)
-  {
-    let target = null;
-    for (let child = element; child; child = child.parentNode) {
-      if (this.hasClass(child, "repTarget"))
-        target = child;
-
-      if (child.repObject) {
-        if (!target && this.hasClass(child.repObject, "repIgnore"))
-          break;
-        else
-          return child.repObject;
-      }
-    }
-    return null;
-  },
-
-  /**
    * @param msg
    *        text message to send to the log
    */
   _log: function LOG(msg)
   {
     Services.console.logStringMessage(msg);
   },
 
@@ -1803,22 +1286,24 @@ var InspectorUI = {
         }
       }.bind(this));
     }
   },
 
   /**
    * For each tool in the tools collection select the current node that is
    * selected in the highlighter
+   * @param aScroll boolean
+   *        Do you want to scroll the treepanel?
    */
-  toolsSelect: function IUI_toolsSelect()
+  toolsSelect: function IUI_toolsSelect(aScroll)
   {
     this.toolsDo(function IUI_toolsOnSelect(aTool) {
       if (aTool.isOpen) {
-        aTool.onSelect.call(aTool.context, InspectorUI.selection);
+        aTool.onSelect.call(aTool.context, InspectorUI.selection, aScroll);
       }
     });
   },
 
   /**
    * Dim or undim each tool in the tools collection
    * @param aState true = dim, false = undim
    */
@@ -1986,17 +1471,17 @@ var InspectorStore = {
  * confirm page navigation, such that he's given the chance to prevent the loss
  * of edits.
  */
 var InspectorProgressListener = {
   onStateChange:
   function IPL_onStateChange(aProgress, aRequest, aFlag, aStatus)
   {
     // Remove myself if the Inspector is no longer open.
-    if (!InspectorUI.isTreePanelOpen) {
+    if (!InspectorUI.isInspectorOpen) {
       gBrowser.removeProgressListener(InspectorProgressListener);
       return;
     }
 
     // Skip non-start states.
     if (!(aFlag & Ci.nsIWebProgressListener.STATE_START)) {
       return;
     }
@@ -2084,20 +1569,24 @@ var InspectorProgressListener = {
     // transient notification removal.
     notification.persistence = -1;
   },
 };
 
 /////////////////////////////////////////////////////////////////////////
 //// Initializers
 
-XPCOMUtils.defineLazyGetter(InspectorUI, "inspectCmd", function () {
+XPCOMUtils.defineLazyGetter(InspectorUI, "inspectMenuitem", function () {
   return document.getElementById("Tools:Inspect");
 });
 
+XPCOMUtils.defineLazyGetter(InspectorUI, "inspectToolbutton", function () {
+  return document.getElementById("inspector-inspect-toolbutton");
+});
+
 XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
   return Services.strings.
          createBundle("chrome://browser/locale/inspector.properties");
 });
 
 XPCOMUtils.defineLazyGetter(InspectorUI, "StyleInspector", function () {
   var obj = {};
   Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj);
--- a/browser/devtools/highlighter/test/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -56,10 +56,12 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_treePanel_result.html \
 		browser_inspector_registertools.js \
 		browser_inspector_bug_665880.js \
 		browser_inspector_bug_674871.js \
 		browser_inspector_editor.js \
 		browser_inspector_bug_566084_location_changed.js \
 		$(NULL)
 
+# 		browser_inspector_treePanel_click.js \
+
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js
@@ -12,17 +12,17 @@ function startLocationTests() {
 function runInspectorTests() {
   Services.obs.removeObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null);
 
   let para = content.document.querySelector("p");
   ok(para, "found the paragraph element");
   is(para.textContent, "init", "paragraph content is correct");
 
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
+  ok(InspectorUI.isInspectorOpen, "Inspector is open");
 
   InspectorUI.isDirty = true;
 
   notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
   notificationBox.addEventListener("AlertActive", alertActive1, false);
 
   gBrowser.selectedBrowser.addEventListener("load", onPageLoad, true);
 
@@ -51,29 +51,29 @@ function onPageLoad() {
   isnot(content.location.href.indexOf("test2"), -1,
         "page navigated to the correct location");
 
   let para = content.document.querySelector("p");
   ok(para, "found the paragraph element, third time");
   is(para.textContent, "test2", "paragraph content is correct");
 
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(!InspectorUI.isTreePanelOpen, "Inspector Panel is not open");
+  ok(!InspectorUI.isInspectorOpen, "Inspector Panel is not open");
 
   testEnd();
 }
 
 function locationTest2() {
   // Location did not change.
   let para = content.document.querySelector("p");
   ok(para, "found the paragraph element, second time");
   is(para.textContent, "init", "paragraph content is correct");
 
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
+  ok(InspectorUI.isInspectorOpen, "Inspector Panel is open");
 
   notificationBox.addEventListener("AlertActive", alertActive2, false);
 
   content.location = "data:text/html,<div>location change test 2 for " +
     "inspector</div><p>test2</p>";
 }
 
 function alertActive2() {
@@ -97,17 +97,16 @@ function alertActive2() {
   // Accept page navigation.
   executeSoon(function(){
     buttonLeave.doCommand();
   });
 }
 
 function testEnd() {
   notificationBox = null;
-  InspectorUI.isDirty = false;
   gBrowser.removeCurrentTab();
   executeSoon(finish);
 }
 
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
--- a/browser/devtools/highlighter/test/browser_inspector_editor.js
+++ b/browser/devtools/highlighter/test/browser_inspector_editor.js
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* ***** BEGIN LICENSE BLOCK *****
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
- * 
+ *
  * Contributor(s):
  *   Rob Campbell <rcampbell@mozilla.com>
  *   Mihai Sucan <mihai.sucan@gmail.com>
- *   Kyle Simpson <ksimpson@mozilla.com> 
+ *   Kyle Simpson <ksimpson@mozilla.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 let doc;
 let div;
 let editorTestSteps;
 
 function doNextStep() {
@@ -20,61 +20,69 @@ function doNextStep() {
 }
 
 function setupEditorTests()
 {
   div = doc.createElement("div");
   div.setAttribute("id", "foobar");
   div.setAttribute("class", "barbaz");
   doc.body.appendChild(div);
-  
-  Services.obs.addObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+  Services.obs.addObserver(setupHTMLPanel, INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
+function setupHTMLPanel()
+{
+  Services.obs.removeObserver(setupHTMLPanel, INSPECTOR_NOTIFICATIONS.OPENED);
+  Services.obs.addObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+  InspectorUI.treePanel.open();
+}
+
 function runEditorTests()
 {
-  Services.obs.removeObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
+  Services.obs.removeObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
   InspectorUI.stopInspecting();
-  
+
   // setup generator for async test steps
   editorTestSteps = doEditorTestSteps();
-  
+
   // add step listeners
   Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
   Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
   Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
 
   // start the tests
   doNextStep();
 }
 
 function doEditorTestSteps()
 {
-  let editor = InspectorUI.treeBrowserDocument.getElementById("attribute-editor");
-  let editorInput = InspectorUI.treeBrowserDocument.getElementById("attribute-editor-input");
+  let treePanel = InspectorUI.treePanel;
+  let editor = treePanel.treeBrowserDocument.getElementById("attribute-editor");
+  let editorInput = treePanel.treeBrowserDocument.getElementById("attribute-editor-input");
 
   // Step 1: grab and test the attribute-value nodes in the HTML panel, then open editor
-  let attrValNode_id = InspectorUI.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0];
-  let attrValNode_class = InspectorUI.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='class']")[0];
+  let attrValNode_id = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0];
+  let attrValNode_class = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='class']")[0];
 
   is(attrValNode_id.innerHTML, "foobar", "Step 1: we have the correct `id` attribute-value node in the HTML panel");
   is(attrValNode_class.innerHTML, "barbaz", "we have the correct `class` attribute-value node in the HTML panel");
-  
+
   // double-click the `id` attribute-value node to open the editor
   executeSoon(function() {
     // firing 2 clicks right in a row to simulate a double-click
     EventUtils.synthesizeMouse(attrValNode_id, 2, 2, {clickCount: 2}, attrValNode_id.ownerDocument.defaultView);
   });
 
   yield; // End of Step 1
 
 
   // Step 2: validate editing session, enter new attribute value into editor, and save input
-  ok(InspectorUI.editingContext, "Step 2: editor session started");
+  ok(InspectorUI.treePanel.editingContext, "Step 2: editor session started");
 
   let editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup visible");
 
   // check if the editor popup is "near" the correct position
   let editorDims = editor.getBoundingClientRect();
   let attrValNodeDims = attrValNode_id.getBoundingClientRect();
   let editorPositionOK = (editorDims.left >= (attrValNodeDims.left - editorDims.width - 5)) &&
@@ -83,35 +91,35 @@ function doEditorTestSteps()
                           (editorDims.bottom <= (attrValNodeDims.bottom + editorDims.height + 5));
 
   ok(editorPositionOK, "editor position acceptable");
 
   // check to make sure the attribute-value node being edited is properly highlighted
   let attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue");
   ok(attrValNodeHighlighted, "`id` attribute-value node is editor-highlighted");
 
-  is(InspectorUI.editingContext.repObj, div, "editor session has correct reference to div");
-  is(InspectorUI.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel");
-  is(InspectorUI.editingContext.attrName, "id", "editor session knows correct attribute-name");
+  is(treePanel.editingContext.repObj, div, "editor session has correct reference to div");
+  is(treePanel.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel");
+  is(treePanel.editingContext.attrName, "id", "editor session knows correct attribute-name");
 
   editorInput.value = "Hello World";
   editorInput.focus();
-  
+
   // hit <enter> to save the inputted value
   executeSoon(function() {
     EventUtils.synthesizeKey("VK_RETURN", {}, attrValNode_id.ownerDocument.defaultView);
   });
 
   // two `yield` statements, to trap both the "SAVED" and "CLOSED" events that will be triggered
   yield;
   yield; // End of Step 2
 
 
   // Step 3: validate that the previous editing session saved correctly, then open editor on `class` attribute value
-  ok(!InspectorUI.editingContext, "Step 3: editor session ended");
+  ok(!treePanel.editingContext, "Step 3: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue");
   ok(!attrValNodeHighlighted, "`id` attribute-value node is no longer editor-highlighted");
   is(div.getAttribute("id"), "Hello World", "`id` attribute-value successfully updated");
   is(attrValNode_id.innerHTML, "Hello World", "attribute-value node in HTML panel successfully updated");
 
   // double-click the `class` attribute-value node to open the editor
@@ -119,93 +127,93 @@ function doEditorTestSteps()
     // firing 2 clicks right in a row to simulate a double-click
     EventUtils.synthesizeMouse(attrValNode_class, 2, 2, {clickCount: 2}, attrValNode_class.ownerDocument.defaultView);
   });
 
   yield; // End of Step 3
 
 
   // Step 4: enter value into editor, then hit <escape> to discard it
-  ok(InspectorUI.editingContext, "Step 4: editor session started");
+  ok(treePanel.editingContext, "Step 4: editor session started");
   editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup visible");
-  
-  is(InspectorUI.editingContext.attrObj, attrValNode_class, "editor session has correct reference to `class` attribute-value node in HTML panel");
-  is(InspectorUI.editingContext.attrName, "class", "editor session knows correct attribute-name");
+
+  is(treePanel.editingContext.attrObj, attrValNode_class, "editor session has correct reference to `class` attribute-value node in HTML panel");
+  is(treePanel.editingContext.attrName, "class", "editor session knows correct attribute-name");
 
   editorInput.value = "Hello World";
   editorInput.focus();
-  
+
   // hit <escape> to discard the inputted value
   executeSoon(function() {
     EventUtils.synthesizeKey("VK_ESCAPE", {}, attrValNode_class.ownerDocument.defaultView);
   });
 
   yield; // End of Step 4
 
 
   // Step 5: validate that the previous editing session discarded correctly, then open editor on `id` attribute value again
-  ok(!InspectorUI.editingContext, "Step 5: editor session ended");
+  ok(!treePanel.editingContext, "Step 5: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   is(div.getAttribute("class"), "barbaz", "`class` attribute-value *not* updated");
   is(attrValNode_class.innerHTML, "barbaz", "attribute-value node in HTML panel *not* updated");
 
   // double-click the `id` attribute-value node to open the editor
   executeSoon(function() {
     // firing 2 clicks right in a row to simulate a double-click
     EventUtils.synthesizeMouse(attrValNode_id, 2, 2, {clickCount: 2}, attrValNode_id.ownerDocument.defaultView);
   });
 
   yield; // End of Step 5
 
 
   // Step 6: validate that editor opened again, then test double-click inside of editor (should do nothing)
-  ok(InspectorUI.editingContext, "Step 6: editor session started");
+  ok(treePanel.editingContext, "Step 6: editor session started");
   editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup visible");
-  
+
   // double-click on the editor input box
   executeSoon(function() {
     // firing 2 clicks right in a row to simulate a double-click
     EventUtils.synthesizeMouse(editorInput, 2, 2, {clickCount: 2}, editorInput.ownerDocument.defaultView);
-    
+
     // since the previous double-click is supposed to do nothing,
     // wait a brief moment, then move on to the next step
     executeSoon(function() {
       doNextStep();
     });
   });
 
   yield; // End of Step 6
 
 
-  // Step 7: validate that editing session is still correct, then enter a value and try a click 
+  // Step 7: validate that editing session is still correct, then enter a value and try a click
   //         outside of editor (should cancel the editing session)
-  ok(InspectorUI.editingContext, "Step 7: editor session still going");
+  ok(treePanel.editingContext, "Step 7: editor session still going");
   editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup still visible");
-  
+
   editorInput.value = "all your base are belong to us";
 
   // single-click the `class` attribute-value node
   executeSoon(function() {
     EventUtils.synthesizeMouse(attrValNode_class, 2, 2, {}, attrValNode_class.ownerDocument.defaultView);
   });
 
   yield; // End of Step 7
 
 
   // Step 8: validate that the editor was closed and that the editing was not saved
-  ok(!InspectorUI.editingContext, "Step 8: editor session ended");
+  ok(!treePanel.editingContext, "Step 8: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   is(div.getAttribute("id"), "Hello World", "`id` attribute-value *not* updated");
   is(attrValNode_id.innerHTML, "Hello World", "attribute-value node in HTML panel *not* updated");
-  
+
   // End of Step 8
 
   // end of all steps, so clean up
   Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
   Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
   Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
 
   executeSoon(finishUp);
--- a/browser/devtools/highlighter/test/browser_inspector_iframeTest.js
+++ b/browser/devtools/highlighter/test/browser_inspector_iframeTest.js
@@ -38,17 +38,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 let doc;
 let div1;
 let div2;
 let iframe1;
 let iframe2;
-let highlighterFrame;
 
 function createDocument()
 {
   doc.title = "Inspector iframe Tests";
 
   iframe1 = doc.createElement('iframe');
 
   iframe1.addEventListener("load", function () {
@@ -94,17 +93,16 @@ function setupIframeTests()
 function runIframeTests()
 {
   Services.obs.removeObserver(runIframeTests,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   Services.obs.addObserver(performTestComparisons1,
     INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
-  highlighterFrame = InspectorUI.highlighter.iframe;
   executeSoon(moveMouseOver.bind(this, div1));
 }
 
 function performTestComparisons1()
 {
   Services.obs.removeObserver(performTestComparisons1,
     INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
   Services.obs.addObserver(performTestComparisons2,
--- a/browser/devtools/highlighter/test/browser_inspector_initialization.js
+++ b/browser/devtools/highlighter/test/browser_inspector_initialization.js
@@ -63,26 +63,37 @@ function startInspectorTests()
   Services.obs.addObserver(runInspectorTests,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function runInspectorTests()
 {
   Services.obs.removeObserver(runInspectorTests,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    INSPECTOR_NOTIFICATIONS.OPENED);
+  Services.obs.addObserver(treePanelTests,
+    INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+
+  ok(InspectorUI.toolbar, "we have the toolbar.");
+  ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
+  ok(InspectorUI.inspecting, "Inspector is inspecting");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  ok(InspectorUI.highlighter, "Highlighter is up");
+
+  InspectorUI.treePanel.open();
+}
+
+function treePanelTests()
+{
+  Services.obs.removeObserver(treePanelTests,
+    INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
   Services.obs.addObserver(runContextMenuTest,
     INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
-  ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
-  let iframe = document.getElementById("inspector-tree-iframe");
-  is(InspectorUI.treeIFrame, iframe, "Inspector IFrame matches");
-  ok(InspectorUI.inspecting, "Inspector is inspecting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
-  ok(InspectorUI.highlighter, "Highlighter is up");
+  ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
 
   executeSoon(function() {
     InspectorUI.closeInspectorUI();
   });
 }
 
 function runContextMenuTest()
 {
@@ -105,17 +116,17 @@ function runContextMenuTest()
 }
 
 function inspectNodesFromContextTest()
 {
   Services.obs.removeObserver(inspectNodesFromContextTest, INSPECTOR_NOTIFICATIONS.OPENED, false);
   Services.obs.addObserver(openInspectorForContextTest, INSPECTOR_NOTIFICATIONS.CLOSED, false);
   ok(!InspectorUI.inspecting, "Inspector is not actively highlighting");
   is(InspectorUI.selection, salutation, "Inspector is highlighting salutation");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
   // TODO: These tests depend on the style inspector patches.
   todo(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
   executeSoon(function() {
     InspectorUI.closeInspectorUI(true);
   });
 }
 
 function openInspectorForContextTest()
@@ -157,19 +168,19 @@ function inspectNodesFromContextTestTrap
 }
 
 function finishInspectorTests()
 {
   Services.obs.removeObserver(finishInspectorTests,
     INSPECTOR_NOTIFICATIONS.CLOSED);
 
   ok(!InspectorUI.highlighter, "Highlighter is gone");
-  ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
+  ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
   ok(!InspectorUI.inspecting, "Inspector is not inspecting");
-  ok(InspectorUI.toolbar.hidden, "toolbar is hidden");
+  ok(!InspectorUI.toolbar, "toolbar is hidden");
 
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
--- a/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
+++ b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
@@ -56,17 +56,17 @@ function inspectorTabOpen1()
 
 function inspectorUIOpen1()
 {
   Services.obs.removeObserver(inspectorUIOpen1,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is open.
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
   ok(!InspectorStore.isEmpty(), "InspectorStore is not empty");
   is(InspectorStore.length, 1, "InspectorStore.length = 1");
 
   // Highlight a node.
   div = content.document.getElementsByTagName("div")[0];
   InspectorUI.inspectNode(div);
   is(InspectorUI.selection, div, "selection matches the div element");
 
@@ -82,17 +82,17 @@ function inspectorUIOpen1()
 
   content.location = "data:text/html,<p>tab 2: the inspector should close now";
 }
 
 function inspectorTabOpen2()
 {
   // Make sure the inspector is closed.
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(!InspectorUI.isPanelOpen, "Inspector Tree Panel is closed");
+  ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
   is(InspectorStore.length, 1, "InspectorStore.length = 1");
 
   // Activate the inspector again.
   executeSoon(function() {
     Services.obs.addObserver(inspectorUIOpen2,
       INSPECTOR_NOTIFICATIONS.OPENED, false);
     InspectorUI.openInspectorUI();
   });
@@ -100,17 +100,17 @@ function inspectorTabOpen2()
 
 function inspectorUIOpen2()
 {
   Services.obs.removeObserver(inspectorUIOpen2,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is open.
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
   is(InspectorStore.length, 2, "InspectorStore.length = 2");
 
   // Disable highlighting.
   InspectorUI.toggleInspection();
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
 
   // Switch back to tab 1.
   executeSoon(function() {
@@ -122,51 +122,100 @@ function inspectorUIOpen2()
 
 function inspectorFocusTab1()
 {
   Services.obs.removeObserver(inspectorFocusTab1,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is still open.
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  is(InspectorStore.length, 2, "InspectorStore.length = 2");
+  is(InspectorUI.selection, div, "selection matches the div element");
+
+  Services.obs.addObserver(inspectorOpenTreePanelTab1,
+    INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+
+  InspectorUI.treePanel.open();
+}
+
+function inspectorOpenTreePanelTab1()
+{
+  Services.obs.removeObserver(inspectorOpenTreePanelTab1,
+    INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
   is(InspectorStore.length, 2, "InspectorStore.length = 2");
   is(InspectorUI.selection, div, "selection matches the div element");
 
   // Switch back to tab 2.
   Services.obs.addObserver(inspectorFocusTab2,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
   gBrowser.selectedTab = tab2;
 }
 
 function inspectorFocusTab2()
 {
   Services.obs.removeObserver(inspectorFocusTab2,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   // Make sure the inspector is still open.
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  is(InspectorStore.length, 2, "InspectorStore.length = 2");
+  isnot(InspectorUI.selection, div, "selection does not match the div element");
+
+  // Switch back to tab 1.
+  Services.obs.addObserver(inspectorSecondFocusTab1,
+    INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+  gBrowser.selectedTab = tab1;
+}
+
+function inspectorSecondFocusTab1()
+{
+  Services.obs.removeObserver(inspectorSecondFocusTab1,
+    INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
+  is(InspectorStore.length, 2, "InspectorStore.length = 2");
+  is(InspectorUI.selection, div, "selection matches the div element");
+
+  // Switch back to tab 2.
+  Services.obs.addObserver(inspectorSecondFocusTab2,
+    INSPECTOR_NOTIFICATIONS.OPENED, false);
+  gBrowser.selectedTab = tab2;
+}
+
+function inspectorSecondFocusTab2()
+{
+  Services.obs.removeObserver(inspectorSecondFocusTab2,
+    INSPECTOR_NOTIFICATIONS.OPENED);
+
+  // Make sure the inspector is still open.
+  ok(!InspectorUI.inspecting, "Inspector is not highlighting");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
   is(InspectorStore.length, 2, "InspectorStore.length = 2");
   isnot(InspectorUI.selection, div, "selection does not match the div element");
 
   // Remove tab 1.
   tab1window = gBrowser.getBrowserForTab(tab1).contentWindow;
   tab1window.addEventListener("pagehide", inspectorTabUnload1, false);
   gBrowser.removeTab(tab1);
 }
 
 function inspectorTabUnload1(evt)
 {
   tab1window.removeEventListener(evt.type, arguments.callee, false);
   tab1window = tab1 = tab2 = div = null;
 
   // Make sure the Inspector is still open and that the state is correct.
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
   is(InspectorStore.length, 1, "InspectorStore.length = 1");
 
   InspectorUI.closeInspectorUI();
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
--- a/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js
+++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js
@@ -12,49 +12,53 @@ function test() {
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     doc = content.document;
     waitForFocus(setupTest, content);
   }, true);
 
-  content.location = "data:text/html,<div><p></p></div>";
+  content.location = 'data:text/html,<div style="width: 200px; height: 200px"><p></p></div>';
 
   function setupTest() {
     node1 = doc.querySelector("div");
     node2 = doc.querySelector("p");
     Services.obs.addObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
     InspectorUI.toggleInspectorUI();
   }
 
   function runTests() {
     Services.obs.removeObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED);
-    testNode1();
+    Services.obs.addObserver(testNode1, INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+    InspectorUI.select(node1, true, true, true);
+    InspectorUI.openTreePanel();
   }
 
   function testNode1() {
-    let box = InspectorUI.ioBox.createObjectBox(node1);
-    box.click();
-    executeSoon(function() {
-      is(InspectorUI.selection, node1, "selection matches node");
-      is(InspectorUI.highlighter.node, node1, "selection matches node");
-      testNode2();
-    });
+    dump("testNode1\n");
+    Services.obs.removeObserver(testNode1, INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
+    is(InspectorUI.selection, node1, "selection matches node");
+    is(InspectorUI.highlighter.node, node1, "selection matches node");
+    testNode2();
   }
 
   function testNode2() {
-    let box = InspectorUI.ioBox.createObjectBox(node2);
-    box.click();
-    executeSoon(function() {
-      is(InspectorUI.selection, node2, "selection matches node");
-      is(InspectorUI.highlighter.node, node2, "selection matches node");
-      Services.obs.addObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED, false);
-      InspectorUI.closeInspectorUI();
-    });
+    dump("testNode2\n")
+    Services.obs.addObserver(testHighlightingNode2, INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.treePanelSelect("node2");
+  }
+
+  function testHighlightingNode2() {
+    dump("testHighlightingNode2\n")
+    Services.obs.removeObserver(testHighlightingNode2, INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    is(InspectorUI.selection, node2, "selection matches node");
+    is(InspectorUI.highlighter.node, node2, "selection matches node");
+    Services.obs.addObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED, false);
+    InspectorUI.closeInspectorUI();
   }
 
   function finishUp() {
     Services.obs.removeObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED);
     doc = node1 = node2 = null;
     gBrowser.removeCurrentTab();
     finish();
   }
--- a/browser/devtools/highlighter/test/browser_inspector_treePanel_output.js
+++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_output.js
@@ -66,53 +66,62 @@ function xhr_onReadyStateChange() {
   Services.obs.addObserver(inspectorOpened,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.openInspectorUI();
 }
 
 function inspectorOpened()
 {
   Services.obs.removeObserver(inspectorOpened,
-    INSPECTOR_NOTIFICATIONS.OPENED, false);
+    INSPECTOR_NOTIFICATIONS.OPENED);
+
+  Services.obs.addObserver(treePanelOpened, INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+  InspectorUI.treePanel.open();
+}
+
+function treePanelOpened()
+{
+  Services.obs.removeObserver(treePanelOpened,
+    INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
 
   ok(InspectorUI.inspecting, "Inspector is highlighting");
-  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
   InspectorUI.stopInspecting();
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
 
   let elements = doc.querySelectorAll("meta, script, style, p[unknownAttribute]");
   for (let i = 0; i < elements.length; i++) {
     InspectorUI.inspectNode(elements[i]);
   }
 
   let iframe = doc.querySelector("iframe");
   ok(iframe, "Found the iframe tag");
   ok(iframe.contentDocument, "Found the iframe.contentDocument");
 
   let iframeDiv = iframe.contentDocument.querySelector("div");
   ok(iframeDiv, "Found the div element inside the iframe");
   InspectorUI.inspectNode(iframeDiv);
 
-  ok(InspectorUI.treePanelDiv, "InspectorUI.treePanelDiv is available");
-  is(InspectorUI.treePanelDiv.innerHTML.replace(/^\s+|\s+$/mg, ''),
+  ok(InspectorUI.treePanel.treePanelDiv, "InspectorUI.treePanelDiv is available");
+  is(InspectorUI.treePanel.treePanelDiv.innerHTML.replace(/^\s+|\s+$/mg, ''),
     expectedResult, "treePanelDiv.innerHTML is correct");
   expectedResult = null;
 
   Services.obs.addObserver(inspectorClosed,
     INSPECTOR_NOTIFICATIONS.CLOSED, false);
   InspectorUI.closeInspectorUI();
 }
 
 function inspectorClosed()
 {
   Services.obs.removeObserver(inspectorClosed,
     INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
-  ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is not open");
+  ok(!InspectorUI.treePanel, "Inspector Tree Panel is not open");
 
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -1,8 +1,9 @@
 browser.jar:
+*   content/browser/inspector.html                (highlighter/inspector.html)
     content/browser/NetworkPanel.xhtml            (webconsole/NetworkPanel.xhtml)
 *   content/browser/scratchpad.xul                (scratchpad/scratchpad.xul)
 *   content/browser/scratchpad.js                 (scratchpad/scratchpad.js)
     content/browser/csshtmltree.xhtml             (styleinspector/csshtmltree.xhtml)
     content/browser/orion.js                      (sourceeditor/orion/orion.js)
     content/browser/orion.css                     (sourceeditor/orion/orion.css)
 *   content/browser/inspector.html                (highlighter/inspector.html)
--- a/browser/devtools/scratchpad/Makefile.in
+++ b/browser/devtools/scratchpad/Makefile.in
@@ -7,21 +7,21 @@
 # the License. You may obtain a copy of the License at
 # http://www.mozilla.org/MPL/
 #
 # Software distributed under the License is distributed on an "AS IS" basis,
 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 # for the specific language governing rights and limitations under the
 # License.
 #
-# The Original Code is  HUDService code.
+# The Original Code is Scratchpad Build Code.
 #
-# The Initial Developer of the Original Code is Mozilla Corporation.
-# 
-# Portions created by the Initial Developer are Copyright (C) 2010
+# The Initial Developer of the Original Code is The Mozilla Foundation.
+#
+# Portions created by the Initial Developer are Copyright (C) 2011
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Rob Campbell <rcampbell@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -39,14 +39,12 @@
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 ifdef ENABLE_TESTS
-ifneq (mobile,$(MOZ_BUILD_APP))
 	DIRS += test
 endif
-endif
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/locales/en-US/chrome/browser/inspector.properties
+++ b/browser/locales/en-US/chrome/browser/inspector.properties
@@ -2,8 +2,23 @@
 # the user tries to navigate away from a web page, to confirm the change of
 # page.
 confirmNavigationAway.message=Leaving this page will close the Inspector and the changes you have made will be lost.
 confirmNavigationAway.buttonLeave=Leave Page
 confirmNavigationAway.buttonLeaveAccesskey=L
 confirmNavigationAway.buttonStay=Stay on Page
 confirmNavigationAway.buttonStayAccesskey=S
 
+# LOCALIZATION NOTE (htmlPanel): Used in the Inspector tool's openInspectorUI
+# method when registering the HTML panel.
+
+# LOCALIZATION NOTE (htmlPanel.label): A button label that appears on the
+# InspectorUI's toolbar.
+htmlPanel.label=HTML
+
+# LOCALIZATION NOTE (htmlPanel.tooltiptext): The text that appears when a user
+# hovers over the HTML panel's toolbar button.
+htmlPanel.tooltiptext=HTML panel
+
+# LOCALIZATION NOTE (htmlPanel.accesskey): The key bound to the HTML panel's
+# toolbar button.
+htmlPanel.accesskey=H
+