Bug 561782 - Create DOM Panel for inspecting DOM nodes and their properties r=gavin, r=l10n, a=blocking
authorRob Campbell <rcampbell@mozilla.com>
Wed, 11 Aug 2010 20:38:02 -0300
changeset 49586 e77d84f9ebe42aa07fd616d79f1b27149e58ccbf
parent 49585 8b3fd544de43c059753d368c773d725c09637808
child 49587 838edca9178f2c56078d721c1f6916f55c92f3f1
push id15017
push userrcampbell@mozilla.com
push dateWed, 11 Aug 2010 23:41:32 +0000
treeherdermozilla-central@ccfdad9f5c93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin, l10n, blocking
bugs561782
milestone2.0b4pre
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 561782 - Create DOM Panel for inspecting DOM nodes and their properties r=gavin, r=l10n, a=blocking
browser/base/content/browser-sets.inc
browser/base/content/browser.xul
browser/base/content/inspector.js
browser/base/content/test/Makefile.in
browser/base/content/test/browser_inspector_domPanel.js
browser/base/content/test/browser_inspector_initialization.js
browser/base/content/test/browser_inspector_stylePanel.js
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/inspector.properties
browser/themes/gnomestripe/browser/browser.css
browser/themes/pinstripe/browser/browser.css
browser/themes/winstripe/browser/browser.css
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -145,18 +145,16 @@
     <command id="Inspector:Inspect"
              oncommand="InspectorUI.toggleInspection();"/>
     <command id="Inspector:Previous"
              oncommand="InspectorUI.inspectPrevious();"
              disabled="true"/>
     <command id="Inspector:Next"
              oncommand="InspectorUI.inspectNext();"
              disabled="true"/>
-    <command id="Inspector:Style"
-             oncommand="InspectorUI.toggleStylePanel();"/>
   </commandset>
 
   <broadcasterset id="mainBroadcasterSet">
     <broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
                  type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
                  oncommand="toggleSidebar('viewBookmarksSidebar');"/>
 
     <!-- for both places and non-places, the sidebar lives at
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -250,17 +250,22 @@
                        label="&inspectNextButton.label;"
                        accesskey="&inspectNextButton.accesskey;"
                        class="toolbarbutton-text"
                        command="Inspector:Next"/>
         <toolbarbutton id="inspector-style-toolbutton"
                        label="&inspectStyleButton.label;"
                        accesskey="&inspectStyleButton.accesskey;"
                        class="toolbarbutton-text"
-                       command="Inspector:Style"/>
+                       oncommand="InspectorUI.toggleStylePanel();'"/>
+        <toolbarbutton id="inspector-dom-toolbutton"
+                       label="&inspectDOMButton.label;"
+                       accesskey="&inspectDOMButton.accesskey;"
+                       class="toolbarbutton-text"
+                       oncommand="InspectorUI.toggleDOMPanel();"/>
       </toolbar>
       <tree id="inspector-tree" class="plain"
             seltype="single"
             treelines="true"
             onselect="InspectorUI.onTreeSelected()"
             flex="1">
         <treecols>
           <treecol id="colNodeName" label="nodeName" primary="true"
@@ -284,17 +289,17 @@
            noautofocus="true"
            noautohide="true"
            level="top"
            titlebar="normal"
            label="&inspectStylePanelTitle.label;">
         <listbox id="inspector-style-listbox" flex="1"/>
         <hbox align="end">
           <spacer flex="1" />
-          <resizer dir="bottomend" />
+          <resizer dir="bottomend"/>
         </hbox>
     </panel>
 
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event);">
       <menuseparator/>
       <menuitem command="cmd_ToggleTabsOnTop"
                 type="checkbox"
--- a/browser/base/content/inspector.js
+++ b/browser/base/content/inspector.js
@@ -56,50 +56,26 @@ const INSPECTOR_INVISIBLE_ELEMENTS = {
 ///////////////////////////////////////////////////////////////////////////
 //// PanelHighlighter
 
 /**
  * A highlighter mechanism using xul panels.
  *
  * @param aBrowser
  *        The XUL browser object for the content window being highlighted.
- * @param aColor
- *        A string containing an RGB color for the panel background.
- * @param aBorderSize
- *        A number representing the border thickness of the panel.
- * @param anOpacity
- *        A number representing the alpha value of the panel background.
  */
-function PanelHighlighter(aBrowser, aColor, aBorderSize, anOpacity)
+function PanelHighlighter(aBrowser)
 {
   this.panel = document.getElementById("highlighter-panel");
   this.panel.hidden = false;
   this.browser = aBrowser;
   this.win = this.browser.contentWindow;
-  this.backgroundColor = aColor;
-  this.border = aBorderSize;
-  this.opacity = anOpacity;
-  this.updatePanelStyles();
 }
 
 PanelHighlighter.prototype = {
-  
-  /**
-   * Update the panel's style object with current settings.
-   * TODO see bugXXXXXX, https://wiki.mozilla.org/Firefox/Projects/Inspector#0.7
-   * and, https://wiki.mozilla.org/Firefox/Projects/Inspector#1.0.
-   */
-  updatePanelStyles: function PanelHighlighter_updatePanelStyles()
-  {
-    let style = this.panel.style;
-    style.backgroundColor = this.backgroundColor;
-    style.border = "solid blue " + this.border + "px";
-    style.MozBorderRadius = "4px";
-    style.opacity = this.opacity;
-  },
 
   /**
    * Highlight this.node, unhilighting first if necessary.
    *
    * @param scroll
    *        Boolean determining whether to scroll or not.
    */
   highlight: function PanelHighlighter_highlight(scroll)
@@ -512,22 +488,16 @@ InspectorTreeView.prototype = {
 ///////////////////////////////////////////////////////////////////////////
 //// InspectorUI
 
 /**
  * Main controller class for the Inspector.
  */
 var InspectorUI = {
   browser: null,
-  _showTreePanel: true,
-  _showStylePanel: true,
-  _showDOMPanel: false,
-  highlightColor: "#EEEE66",
-  highlightThickness: 4,
-  highlightOpacity: 0.4,
   selectEventsSuppressed: false,
   inspecting: false,
 
   /**
    * Toggle the inspector interface elements on or off.
    *
    * @param aEvent
    *        The event that requested the UI change. Toolbar button or menu.
@@ -554,25 +524,39 @@ var InspectorUI = {
     }
   },
 
   /**
    * Toggle the style panel. Invoked from the toolbar's Style button.
    */
   toggleStylePanel: function IUI_toggleStylePanel()
   {
-    if (this._showStylePanel) {
+    if (this.isStylePanelOpen) {
       this.stylePanel.hidePopup();
     } else {
       this.openStylePanel();
       if (this.treeView.selectedNode) {
         this.updateStylePanel(this.treeView.selectedNode);
       }
     }
-    this._showStylePanel = !this._showStylePanel;
+  },
+
+  /**
+   * Toggle the DOM panel. Invoked from the toolbar's DOM button.
+   */
+  toggleDOMPanel: function IUI_toggleDOMPanel()
+  {
+    if (this.isDOMPanelOpen) {
+      this.domPanel.hidePopup();
+    } else {
+      this.openDOMPanel();
+      if (this.treeView.selectedNode) {
+        this.updateDOMPanel(this.treeView.selectedNode);
+      }
+    }
   },
 
   /**
    * Is the tree panel open?
    *
    * @returns boolean
    */
   get isPanelOpen()
@@ -586,116 +570,141 @@ var InspectorUI = {
    * @returns boolean
    */
   get isStylePanelOpen()
   {
     return this.stylePanel && this.stylePanel.state == "open";
   },
 
   /**
+   * Is the DOM panel open?
+   *
+   * @returns boolean
+   */
+  get isDOMPanelOpen()
+  {
+    return this.domPanel && this.domPanel.state == "open";
+  },
+
+  /**
    * Open the inspector's tree panel and initialize it.
    */
   openTreePanel: function IUI_openTreePanel()
   {
     if (!this.treePanel) {
       this.treePanel = document.getElementById("inspector-panel");
       this.treePanel.hidden = false;
     }
     if (!this.isPanelOpen) {
       const panelWidthRatio = 7 / 8;
       const panelHeightRatio = 1 / 5;
       let bar = document.getElementById("status-bar");
-      this.treePanel.openPopup(bar, "overlap", 120, -120, false, false);
+      this.treePanel.openPopupAtScreen(this.win.screenX + 80,
+        this.win.outerHeight + this.win.screenY);
       this.treePanel.sizeTo(this.win.outerWidth * panelWidthRatio, 
         this.win.outerHeight * panelHeightRatio);
       this.tree = document.getElementById("inspector-tree");
       this.createDocumentModel();
     }
   },
 
   /**
    * Open the style panel if not already onscreen.
    */
   openStylePanel: function IUI_openStylePanel()
   {
-    if (!this.stylePanel) {
+    if (!this.stylePanel)
       this.stylePanel = document.getElementById("inspector-style-panel");
+    if (!this.isStylePanelOpen) {
       this.stylePanel.hidden = false;
-    }
-    if (!this.isStylePanelOpen) {
       // open at top right of browser panel, offset by 20px from top.
       this.stylePanel.openPopup(this.browser, "end_before", 0, 20, false, false);
       // size panel to 200px wide by half browser height - 60.
       this.stylePanel.sizeTo(200, this.win.outerHeight / 2 - 60);
     }
   },
 
   /**
+   * Open the DOM panel if not already onscreen.
+   */
+  openDOMPanel: function IUI_openDOMPanel()
+  {
+    if (!this.isDOMPanelOpen) {
+      // open at middle right of browser panel, offset by 20px from middle.
+      this.domPanel.openPopup(this.browser, "end_before", 0,
+        this.win.outerHeight / 2 - 20, false, false);
+      // size panel to 200px wide by half browser height - 60.
+      this.domPanel.sizeTo(200, this.win.outerHeight / 2 - 60);
+    }
+  },
+
+  /**
    * Toggle the dimmed (semi-transparent) state for a panel by setting or
    * removing a dimmed attribute.
    *
    * @param aDim
    *        The panel to be dimmed.
    */
   toggleDimForPanel: function IUI_toggleDimForPanel(aDim)
   {
     if (aDim.hasAttribute("dimmed")) {
       aDim.removeAttribute("dimmed");
     } else {
       aDim.setAttribute("dimmed", "true");
     }
   },
 
-  openDOMPanel: function IUI_openDOMPanel()
-  {
-    // # todo bug 561782
-  },
-
   /**
    * Open inspector UI. tree, style and DOM panels if enabled. Add listeners for
    * document scrolling, resize and tabContainer.TabSelect.
    */
   openInspectorUI: function IUI_openInspectorUI()
   {
     // initialization
     this.browser = gBrowser.selectedBrowser;
     this.win = this.browser.contentWindow;
-    if (!this.style) {
-      Cu.import("resource:///modules/stylePanel.jsm", this);
-      this.style.initialize();
-    }
+
+    // DOM panel initialization and loading (via PropertyPanel.jsm)
+    let domPanelTitle = this.strings.GetStringFromName("dom.domPanelTitle");
+    let parent = document.getElementById("inspector-style-panel").parentNode;
+    let propPanel = new (this.PropertyPanel)(parent, document, domPanelTitle, {});
+
+    // additional DOM panel setup needed for unittest identification and use
+    this.domPanel = propPanel.panel;
+    this.domPanel.setAttribute("id", "inspector-dom-panel");
+    this.domBox = propPanel.tree;
+    this.domTreeView = propPanel.treeView;
 
     // open inspector UI
-    if (this._showTreePanel) {
-      this.openTreePanel();
-    }
-    if (this._showStylePanel) {
-      this.styleBox = document.getElementById("inspector-style-listbox");
-      this.clearStylePanel();
-      this.openStylePanel();
-    }
-    if (this._showDOMPanel) {
-      this.openDOMPanel();
-    }
-    this.inspectorBundle = Services.strings.createBundle("chrome://browser/locale/inspector.properties");
+    this.openTreePanel();
+
+    // style panel setup and activation
+    this.styleBox = document.getElementById("inspector-style-listbox");
+    this.clearStylePanel();
+    this.openStylePanel();
+
+    // DOM panel setup and activation
+    this.clearDOMPanel();
+    this.openDOMPanel();
+
+    // setup highlighter and start inspecting
     this.initializeHighlighter();
     this.startInspecting();
     this.win.document.addEventListener("scroll", this, false);
     this.win.addEventListener("resize", this, false);
     gBrowser.tabContainer.addEventListener("TabSelect", this, false);
     this.inspectCmd.setAttribute("checked", true);
   },
 
   /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
-    this.highlighter = new PanelHighlighter(this.browser, this.highlightColor,
-      this.highlightThickness, this.highlightOpacity);
+    this.highlighter = new PanelHighlighter(this.browser);
   },
 
   /**
    * Close inspector UI and associated panels. Unhighlight and stop inspecting.
    * Remove event listeners for document scrolling, resize and
    * tabContainer.TabSelect.
    */
   closeInspectorUI: function IUI_closeInspectorUI()
@@ -709,94 +718,113 @@ var InspectorUI = {
     }
     if (this.isPanelOpen) {
       this.treePanel.hidePopup();
       this.treeView.destroy();
     }
     if (this.isStylePanelOpen) {
       this.stylePanel.hidePopup();
     }
+    if (this.isDOMPanelOpen) {
+      this.domPanel.hidePopup();
+    }
     this.inspectCmd.setAttribute("checked", false);
     this.browser = this.win = null; // null out references to browser and window
   },
 
   /**
    * Begin inspecting webpage, attach page event listeners, activate
    * highlighter event listeners.
    */
   startInspecting: function IUI_startInspecting()
   {
     this.attachPageListeners();
     this.inspecting = true;
     this.toggleDimForPanel(this.stylePanel);
+    this.toggleDimForPanel(this.domPanel);
   },
 
   /**
    * Stop inspecting webpage, detach page listeners, disable highlighter
    * event listeners.
    */
   stopInspecting: function IUI_stopInspecting()
   {
     if (!this.inspecting)
       return;
     this.detachPageListeners();
     this.inspecting = false;
     this.toggleDimForPanel(this.stylePanel);
+    this.toggleDimForPanel(this.domPanel);
     if (this.treeView.selection) {
       this.updateStylePanel(this.treeView.selectedNode);
+      this.updateDOMPanel(this.treeView.selectedNode);
     }
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Model Creation Methods
 
   /**
    * Create treeView object from content window.
    */
   createDocumentModel: function IUI_createDocumentModel()
   {
     this.treeView = new InspectorTreeView(this.win);
   },
 
   /**
-   * add a new item to the listbox
+   * Add a new item to the style panel listbox.
    *
    * @param aLabel
    *        A bit of text to put in the listitem's label attribute.
    * @param aType
    *        The type of item.
    * @param content
    *        Text content or value of the listitem.
    */
   addStyleItem: function IUI_addStyleItem(aLabel, aType, aContent)
   {
-    let itemLabelString = this.inspectorBundle.GetStringFromName("style.styleItemLabel");
+    let itemLabelString = this.strings.GetStringFromName("style.styleItemLabel");
     let item = document.createElement("listitem");
 
     // Do not localize these strings
     let label = aLabel;
     item.className = "style-" + aType;
     if (aContent) {
       label = itemLabelString.replace("#1", aLabel);
       label = label.replace("#2", aContent);
     }
     item.setAttribute("label", label);
 
     this.styleBox.appendChild(item);
   },
 
   /**
+   * Add a new item to the DOM panel listbox
+   *
+   * @param aPair
+   *        an object with name and value properties.
+   */
+  addDOMItem: function IUI_addDOMItem(aPair)
+  {
+    let item = document.createElement("listitem");
+    item.setAttribute("label", aPair.name + ": " + aPair.value);
+    this.domBox.appendChild(item);
+  },
+
+  /**
    * Create items for each rule included in the given array.
    *
    * @param aRules
    *        an array of rule objects
    */
   createStyleRuleItems: function IUI_createStyleRuleItems(aRules)
   {
-    let selectorLabel = this.inspectorBundle.GetStringFromName("style.selectorLabel");
+    let selectorLabel = this.strings.GetStringFromName("style.selectorLabel");
 
     aRules.forEach(function(rule) {
       this.addStyleItem(selectorLabel, "selector", rule.id);
       rule.properties.forEach(function(property) {
         if (property.overridden)
           return; // property marked overridden elsewhere
         // Do not localize the strings below this line
         let important = "";
@@ -816,17 +844,17 @@ var InspectorUI = {
    * @param aSections
    *        Array of sections encapsulating the inherited rules for selectors
    *        and elements.
    */
   createStyleItems: function IUI_createStyleItems(aRules, aSections)
   {
     this.createStyleRuleItems(aRules);
     let inheritedString = 
-        this.inspectorBundle.GetStringFromName("style.inheritedFrom");
+        this.strings.GetStringFromName("style.inheritedFrom");
     aSections.forEach(function(section) {
       let sectionTitle = section.element.tagName;
       if (section.element.id)
         sectionTitle += "#" + section.element.id;
       let replacedString = inheritedString.replace("#1", sectionTitle);
       this.addStyleItem(replacedString, "section");
       this.createStyleRuleItems(section.rules);
     }, this);
@@ -837,33 +865,56 @@ var InspectorUI = {
    */
   clearStylePanel: function IUI_clearStylePanel()
   {
     for (let i = this.styleBox.childElementCount; i >= 0; --i)
       this.styleBox.removeItemAt(i);
   },
 
   /**
+   * Remove all items from the DOM Panel's listbox.
+   */
+  clearDOMPanel: function IUI_clearStylePanel()
+  {
+    this.domTreeView.data = {};
+  },
+
+  /**
    * Update the contents of the style panel with styles for the currently
    * inspected node.
    *
    * @param aNode
    *        The highlighted node to get styles for.
    */
   updateStylePanel: function IUI_updateStylePanel(aNode)
   {
-    if (this.inspecting || !this.isStylePanelOpen)
+    if (this.inspecting || !this.isStylePanelOpen) {
       return;
+    }
+
     let rules = [], styleSections = [], usedProperties = {};
     this.style.getInheritedRules(aNode, styleSections, usedProperties);
     this.style.getElementRules(aNode, rules, usedProperties);
     this.clearStylePanel();
     this.createStyleItems(rules, styleSections);
   },
 
+  /**
+   * Update the contents of the DOM panel with name/value pairs for the
+   * currently-inspected node.
+   */
+  updateDOMPanel: function IUI_updateDOMPanel(aNode)
+  {
+    if (this.inspecting || !this.isDOMPanelOpen) {
+      return;
+    }
+
+    this.domTreeView.data = aNode;
+  },
+
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
 
   /**
    * Main callback handler for events.
    *
    * @param event
    *        The event to be handled.
@@ -903,21 +954,21 @@ var InspectorUI = {
    * Event fired when a tree row is selected in the tree panel.
    */
   onTreeSelected: function IUI_onTreeSelected()
   {
     if (this.selectEventsSuppressed) {
       return false;
     }
 
-    let treeView = this.treeView;
-    let node = treeView.selectedNode;
+    let node = this.treeView.selectedNode;
     this.highlighter.highlightNode(node);
     this.stopInspecting();
     this.updateStylePanel(node);
+    this.updateDOMPanel(node);
     return true;
   },
 
   /**
    * Attach event listeners to content window and child windows to enable 
    * highlighting and click to stop inspection.
    */
   attachPageListeners: function IUI_attachPageListeners()
@@ -950,16 +1001,17 @@ var InspectorUI = {
    */
   inspectNode: function IUI_inspectNode(aNode)
   {
     this.highlighter.highlightNode(aNode);
     this.selectEventsSuppressed = true;
     this.treeView.selectedNode = aNode;
     this.selectEventsSuppressed = false;
     this.updateStylePanel(aNode);
+    this.updateDOMPanel(aNode);
   },
 
   /**
    * Find an element from the given coordinates. This method descends through 
    * frames to find the element the user clicked inside frames.
    *
    * @param DOMDocument aDocument the document to look into.
    * @param integer aX
@@ -995,12 +1047,37 @@ var InspectorUI = {
    *        text message to send to the log
    */
   _log: function LOG(msg)
   {
     Services.console.logStringMessage(msg);
   },
 }
 
+/////////////////////////////////////////////////////////////////////////
+//// Initializors
+
 XPCOMUtils.defineLazyGetter(InspectorUI, "inspectCmd", function () {
   return document.getElementById("Tools:Inspect");
 });
 
+XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
+  return Services.strings.createBundle("chrome://browser/locale/inspector.properties");
+});
+
+XPCOMUtils.defineLazyGetter(InspectorUI, "PropertyTreeView", function () {
+  var obj = {};
+  Cu.import("resource://gre/modules/PropertyPanel.jsm", obj);
+  return obj.PropertyTreeView;
+});
+
+XPCOMUtils.defineLazyGetter(InspectorUI, "PropertyPanel", function () {
+  var obj = {};
+  Cu.import("resource://gre/modules/PropertyPanel.jsm", obj);
+  return obj.PropertyPanel;
+});
+
+XPCOMUtils.defineLazyGetter(InspectorUI, "style", function () {
+  var obj = {};
+  Cu.import("resource:///modules/stylePanel.jsm", obj);
+  obj.style.initialize();
+  return obj.style;
+});
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -145,16 +145,17 @@ endif
                  browser_discovery.js \
                  browser_drag.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_inspector_initialization.js \
                  browser_inspector_treeSelection.js \
                  browser_inspector_highlighter.js \
                  browser_inspector_stylePanel.js \
+                 browser_inspector_domPanel.js \
                  browser_inspector_iframeTest.js \
                  browser_inspector_scrolling.js \
                  browser_pageInfo.js \
                  browser_page_style_menu.js \
                  browser_pinnedTabs.js \
                  browser_plainTextLinks.js \
                  browser_pluginnotification.js \
                  browser_popupUI.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_inspector_domPanel.js
@@ -0,0 +1,128 @@
+/* -*- 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 Inspector DOM Panel Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * 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"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+let doc;
+let testGen;
+
+function createDocument()
+{
+  doc.body.innerHTML = '<div id="first" style="{ margin: 10em; ' +
+    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA}">\n' +
+    '<h1>Some header text</h1>\n' +
+    '<p id="salutation" style="{font-size: 12pt}">hi.</p>\n' +
+    '<p id="body" style="{font-size: 12pt}">I am a test-case. This text exists ' +
+    'solely to provide some things to <span style="{color: yellow}">' +
+    'highlight</span> and <span style="{font-weight: bold}">count</span> ' +
+    'DOM list-items in the box at right. If you are reading this, ' +
+    'you should go do something else instead. Maybe read a book. Or better ' +
+    'yet, write some test-cases for another bit of code. ' +
+    '<span style="{font-style: italic}">Maybe more inspector test-cases!</span></p>\n' +
+    '<p id="closing">end transmission</p>\n' +
+    '</div>';
+  doc.title = "Inspector DOM Test";
+  document.addEventListener("popupshown", runDOMTests, false);
+  InspectorUI.openInspectorUI();
+}
+
+function nodeGenerator()
+{
+  let body = doc.body;
+  InspectorUI.inspectNode(body);
+  yield;
+  let h1 = doc.querySelector("h1");
+  InspectorUI.inspectNode(h1);
+  yield;
+  let first = doc.getElementById("first");
+  InspectorUI.inspectNode(first);
+  yield;
+  let closing = doc.getElementById("#closing");
+  InspectorUI.inspectNode(closing);
+  yield;
+}
+
+function runDOMTests(evt)
+{
+  if (evt.target.id != "inspector-dom-panel")
+    return true;
+  InspectorUI._log("runDOMtests");
+  document.removeEventListener("popupshown", runDOMTests, false);
+  InspectorUI.stopInspecting();
+  document.addEventListener("popupshown", performTestComparisons, false);
+  testGen = nodeGenerator();
+  testGen.next();
+}
+
+function performTestComparisons(evt)
+{
+  InspectorUI._log("performTestComparisons");
+  if (evt.target.id != "highlighter-panel")
+    return true;
+
+  ok(InspectorUI.treeView.selectedNode, "selection");
+  ok(InspectorUI.isDOMPanelOpen, "DOM panel is open?");
+  ok(InspectorUI.highlighter.isHighlighting, "panel is highlighting");
+  ok(InspectorUI.domTreeView.rowCount > 0, "domBox has items");
+
+  try {
+    testGen.next();
+  } catch(StopIteration) {
+    document.removeEventListener("popupshown", performTestComparisons, false);
+    finishUp();
+  }
+}
+
+function finishUp() {
+  InspectorUI.closeInspectorUI();
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+  
+  content.location = "data:text/html,basic tests for inspector";
+}
+
--- a/browser/base/content/test/browser_inspector_initialization.js
+++ b/browser/base/content/test/browser_inspector_initialization.js
@@ -43,30 +43,30 @@ function startInspectorTests()
 {
   ok(InspectorUI, "InspectorUI variable exists");
   document.addEventListener("popupshown", runInspectorTests, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function runInspectorTests(evt)
 {
-  if (evt.target.id != "inspector-panel")
+  if (evt.target.id != "inspector-dom-panel")
     return true;
   document.removeEventListener("popupshown", runInspectorTests, false);
   document.addEventListener("popuphidden", finishInspectorTests, false);
   ok(InspectorUI.inspecting, "Inspector is highlighting");
   ok(InspectorUI.isPanelOpen, "Inspector Tree Panel is open");
   ok(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
-  todo(InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is open");
+  ok(InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is open");
   InspectorUI.toggleInspectorUI();
 }
 
 function finishInspectorTests(evt)
 {
-  if (evt.target.id != "inspector-style-panel")
+  if (evt.target.id != "inspector-dom-panel")
     return true;
   document.removeEventListener("popuphidden", finishInspectorTests, false);
   ok(!InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is closed");
   ok(!InspectorUI.isStylePanelOpen, "Inspector Style Panel is closed");
   ok(!InspectorUI.isPanelOpen, "Inspector Tree Panel is closed");
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
   gBrowser.removeCurrentTab();
   finish();
--- a/browser/base/content/test/browser_inspector_stylePanel.js
+++ b/browser/base/content/test/browser_inspector_stylePanel.js
@@ -88,18 +88,17 @@ function runStyleTests(evt)
 }
 
 function performTestComparisons(evt)
 {
   if (evt.target.id != "highlighter-panel")
     return true;
 
   ok(InspectorUI.treeView.selectedNode, "selection");
-  ok(InspectorUI._showStylePanel, "_showStylePanel");
-  is(InspectorUI.isStylePanelOpen, InspectorUI._showStylePanel, "style panel matches _showStylePanel?");
+  ok(InspectorUI.isStylePanelOpen, "style panel is open?");
   ok(InspectorUI.highlighter.isHighlighting, "panel is highlighting");
   ok(InspectorUI.styleBox.itemCount > 0, "styleBox has items");
 
   try {
     testGen.next();
   } catch(StopIteration) {
     document.removeEventListener("popupshown", performTestComparisons, false);
     finishUp();
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -181,16 +181,22 @@
 <!ENTITY inspectButton.accesskey      "I">
 <!ENTITY inspectNextButton.label      "Next">
 <!ENTITY inspectNextButton.accesskey  "N">
 <!ENTITY inspectPreviousButton.label  "Previous">
 <!ENTITY inspectPreviousButton.accesskey "P">
 <!ENTITY inspectStyleButton.label     "Style">
 <!ENTITY inspectStyleButton.accesskey "S">
 <!ENTITY inspectStylePanelTitle.label  "Style">
+<!-- LOCALIZATION NOTE (inspectDOMButton.label): This button label
+  -  stands for Document Object Model and appears on the inspector's toolbar.
+  -  It is used to open and closed the DOM panel. There is also a DOM label in
+  -  inspector.properties for the panel titlebar. -->
+<!ENTITY inspectDOMButton.label       "DOM">
+<!ENTITY inspectDOMButton.accesskey       "D">
 
 <!ENTITY fileMenu.label         "File"> 
 <!ENTITY fileMenu.accesskey       "F">
 <!ENTITY newNavigatorCmd.label        "New Window">
 <!ENTITY newNavigatorCmd.key        "N">
 <!ENTITY newNavigatorCmd.accesskey      "N">
 
 <!ENTITY editMenu.label         "Edit"> 
--- a/browser/locales/en-US/chrome/browser/inspector.properties
+++ b/browser/locales/en-US/chrome/browser/inspector.properties
@@ -1,11 +1,17 @@
 # LOCALIZATION NOTE  (style.selectorLabel): Used in the Inspector style panel
 #  to label a CSS Selector.
 style.selectorLabel=Selector
 
 # LOCALIZATION NOTE  (style.inheritedFrom): used in Style panel in
 #  inspector. Describes which tagname[#id] the properties are inherited from.
 style.inheritedFrom=Inherited from: #1
 
-# LOCALIZATION NOTE (style.styleItemLabel: used in Style panel in inspector.
+# LOCALIZATION NOTE (style.styleItemLabel): used in Style panel in inspector.
 #  Used for construction of list items, #1 = label, #2 = content.
 style.styleItemLabel=#1: #2
+
+# LOCALIZATION NOTE (dom.domPanelTitle): used in DOM Panel in inspector.
+#  Stands for "Document Object Model". Also referenced in in browser.dtd
+#  and used as a button title.
+#  Unsure if this localizes well, but including just in case
+dom.domPanelTitle=DOM
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -1501,16 +1501,19 @@ toolbar[mode="text"] toolbarbutton.chevr
   -moz-transform: translate(0, 2px);
 }
 
 /* Inspector / Highlighter */
 
 #highlighter-panel {
   -moz-appearance: none;
   -moz-window-shadow: none;
+  background: -moz-linear-gradient(top -1deg, #ffdd88, #ffeeaa);
+  border: none;
+  opacity: 0.35;
 }
 
 listitem.style-selector {
   background-color: DarkGray;
   color: white;
 }
 
 listitem.style-section {
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -2081,16 +2081,19 @@ toolbarbutton.chevron > .toolbarbutton-m
   -moz-box-shadow: @focusRingShadow@;
 }
 
 /* Inspector / Highlighter */
 
 #highlighter-panel {
   -moz-appearance: none;
   -moz-window-shadow: none;
+  background: -moz-linear-gradient(top -1deg, #ffdd88, #ffeeaa);
+  border: none;
+  opacity: 0.35;
 }
 
 listitem.style-selector {
   background-color: DarkGray;
   color: white;
 }
 
 listitem.style-section {
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -1790,16 +1790,19 @@ toolbarbutton.bookmark-item[dragover="tr
   outline: 1px dotted -moz-dialogText;
 }
 
 /* Inspector / Highlighter */
 
 #highlighter-panel {
   -moz-appearance: none;
   -moz-window-shadow: none;
+  background: -moz-linear-gradient(top -1deg, #ffdd88, #ffeeaa);
+  border: none;
+  opacity: 0.35;
 }
 
 listitem.style-selector {
   background-color: DarkGray;
   color: white;
 }
 
 listitem.style-section {