merge the last green changeset on m-c to fx-team
authorTim Taubert <tim.taubert@gmx.de>
Sat, 22 Oct 2011 21:07:26 +0200
changeset 79120 72bb20c484a2e0e6c796aed85544d7c6c5b9abe6
parent 79119 9fa7d2c8ec2d458c2bc9395e5a89973d3db52d1a (current diff)
parent 79062 372c8e0fedccfdadfec86265f78459f0dd09b571 (diff)
child 79121 5df0d42376702f49f6912d8a019f626a2241eee7
child 79124 66ab3bbae4c5980afc688ecc691bd32e11d78433
push id247
push usertim.taubert@gmx.de
push dateSat, 22 Oct 2011 19:08:15 +0000
treeherderfx-team@72bb20c484a2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone10.0a1
merge the last green changeset on m-c to fx-team
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3424,16 +3424,17 @@ const BrowserSearch = {
     // SearchService._addEngineToStore() should fail for such an engine),
     // but let's be on the safe side.
     if (!submission)
       return;
 
     openLinkIn(submission.uri.spec,
                useNewTab ? "tab" : "current",
                { postData: submission.postData,
+                 inBackground: false,
                  relatedToCurrent: true });
   },
 
   /**
    * Returns the search bar element if it is present in the toolbar, null otherwise.
    */
   get searchBar() {
     return document.getElementById("searchbar");
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -971,17 +971,16 @@
 
   <vbox id="browser-bottombox" layer="true">
     <toolbar id="inspector-toolbar"
              nowindowdrag="true"
              hidden="true">
       <toolbarbutton id="inspector-inspect-toolbutton"
                      label="&inspectButton.label;"
                      accesskey="&inspectButton.accesskey;"
-                     class="toolbarbutton-text"
                      command="Inspector:Inspect"/>
       <toolbarseparator />
       <hbox id="inspector-tools">
         <!-- registered tools go here -->
       </hbox>
     </toolbar>
     <toolbar id="addon-bar"
              toolbarname="&addonBarCmd.label;" accesskey="&addonBarCmd.accesskey;"
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -84,16 +84,18 @@ var gCookiesWindow = {
           window.arguments[0].filterString)
         this.setFilter(window.arguments[0].filterString);
     }
     else {
       if (document.getElementById("filter").value != "")
         this.filter();
     }
 
+    document.getElementById("removeAllCookies").disabled = this._view._rowCount == 0;
+
     this._saveState();
   },
 
   _cookieEquals: function (aCookieA, aCookieB, aStrippedHost) {
     return aCookieA.rawHost == aStrippedHost &&
            aCookieA.name == aCookieB.name &&
            aCookieA.path == aCookieB.path;
   },
@@ -112,16 +114,17 @@ var gCookiesWindow = {
     else if (aData == "cleared") {
       this._hosts = {};
       this._hostOrder = [];
 
       var oldRowCount = this._view._rowCount;
       this._view._rowCount = 0;
       this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount);
       this._view.selection.clearSelection();
+      document.getElementById("removeAllCookies").disabled = true;
     }
     else if (aData == "reload") {
       // first, clear any existing entries
       this.observe(aCookie, aTopic, "cleared");
 
       // then, reload the list
       this._populateList(false);
     }
@@ -202,17 +205,20 @@ var gCookiesWindow = {
       }
     }
     // Now update the tree display at the end (we could/should re run the sort
     // if any to get the position correct.)
     var oldRowCount = this._rowCount;
     this._view._rowCount += rowCountImpact;
     this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact);
 
-    document.getElementById("removeAllCookies").disabled = this._view._filtered;
+    if (this._view._rowCount > 0 && !this._view._filtered)
+      document.getElementById("removeAllCookies").disabled = false;
+    else
+      document.getElementById("removeAllCookies").disabled = true;
   },
 
   _view: {
     _filtered   : false,
     _filterSet  : [],
     _filterValue: "",
     _rowCount   : 0,
     _cacheValid : 0,
--- a/browser/components/sessionstore/test/browser/browser_607016.js
+++ b/browser/components/sessionstore/test/browser/browser_607016.js
@@ -84,19 +84,22 @@ function test() {
 
     let curState = JSON.parse(ss.getBrowserState());
     for (let i = 0; i < curState.windows[0].tabs.length; i++) {
       if (state.windows[0].tabs[i].extData) {
         is(curState.windows[0].tabs[i].extData["uniq"],
            state.windows[0].tabs[i].extData["uniq"],
            "sanity check that tab has correct extData");
       }
-      else
+      else {
         ok(!("extData" in curState.windows[0].tabs[i]),
            "sanity check that tab doesn't have extData");
+        //XXXzpao output the tab state to help debug bug 679590
+        info("tabState: " + JSON.stringify(curState.windows[0].tabs[i]));
+      }
     }
 
     // Now we'll set a new unique value on 1 of the tabs
     let newUniq = r();
     ss.setTabValue(gBrowser.tabs[1], "uniq", newUniq);
     gBrowser.removeTab(gBrowser.tabs[1]);
     let closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
     is(closedTabData.state.extData.uniq, newUniq,
--- a/browser/components/tabview/test/browser_tabview_bug591706.js
+++ b/browser/components/tabview/test/browser_tabview_bug591706.js
@@ -33,24 +33,25 @@ function onTabViewWindowLoaded() {
   ok(group.isEmpty(), "This group is empty");
   contentWindow.UI.setActive(group);
   
   // Create a second tab in this new group
   let secondTab = gBrowser.loadOneTab("about:blank#2", {inBackground: true});
   let secondTabItem = secondTab._tabViewTabItem;
   ok(group.getChildren().some(function(child) child == secondTabItem),"The second tab was made in our new group");
   is(group.getChildren().length, 1, "Only one tab in the first group");
-  isnot(firstTab.linkedBrowser.contentWindow.location, secondTab.linkedBrowser.contentWindow.location, "The two tabs must have different locations");
+  isnot(firstTab.linkedBrowser.currentURI.spec, secondTab.linkedBrowser.currentURI.spec, "The two tabs must have different locations");
 
   // Add the first tab to the group *programmatically*, without specifying a dropPos
   group.add(firstTabItem);
   is(group.getChildren().length, 2, "Two tabs in the group");
-  is(group.getChildren()[0].tab.linkedBrowser.contentWindow.location, secondTab.linkedBrowser.contentWindow.location, "The second tab was there first");
-  is(group.getChildren()[1].tab.linkedBrowser.contentWindow.location, firstTab.linkedBrowser.contentWindow.location, "The first tab was just added and went to the end of the line");
-  
+
+  is(group.getChildren()[0].tab.linkedBrowser.currentURI.spec, secondTab.linkedBrowser.currentURI.spec, "The second tab was there first");
+  is(group.getChildren()[1].tab.linkedBrowser.currentURI.spec, firstTab.linkedBrowser.currentURI.spec, "The first tab was just added and went to the end of the line");
+
   group.addSubscriber("close", function onClose() {
     group.removeSubscriber("close", onClose);
 
     ok(group.isEmpty(), "The group is empty again");
 
     is(contentWindow.GroupItems.getActiveGroupItem(), currentGroup, "There is an active group");
     is(gBrowser.tabs.length, 1, "There is only one tab left");
     is(gBrowser.visibleTabs.length, 1, "There is also only one visible tab");
--- a/browser/components/tabview/test/browser_tabview_bug626455.js
+++ b/browser/components/tabview/test/browser_tabview_bug626455.js
@@ -17,35 +17,34 @@ let activeGroup;
 
 function test() {
   waitForExplicitFinish();
 
   showTabView(function () {
     contentWindow = TabView.getContentWindow();
     activeGroup = contentWindow.GroupItems.getActiveGroupItem();
 
-    gBrowser.browsers[0].contentWindow.location =
-      "data:text/html,<p>test for bug 626455, tab1";
+    gBrowser.browsers[0].loadURI("data:text/html,<p>test for bug 626455, tab1");
     gBrowser.addTab(TEST_URL);
 
     afterAllTabsLoaded(testStayOnPage);
   });
 }
 
 function testStayOnPage() {
   whenDialogOpened(function (dialog) {
     // stay on page
     dialog.cancelDialog();
 
     executeSoon(function () {
       showTabView(function () {
         is(gBrowser.tabs.length, 1,
            "The total number of tab is 1 when staying on the page");
 
-        let location = gBrowser.browsers[0].contentWindow.location.toString();
+        let location = gBrowser.browsers[0].currentURI.spec;
         isnot(location.indexOf("onbeforeunload"), -1,
               "The open tab is the expected one");
 
         is(contentWindow.GroupItems.getActiveGroupItem(), activeGroup,
            "Active group is still the same");
 
         is(contentWindow.GroupItems.groupItems.length, 1,
            "Only one group is open");
@@ -75,17 +74,17 @@ function testLeavePage() {
 }
 
 function finishTest() {
   is(gBrowser.tabs.length, 1,
      "The total number of tab is 1 after leaving the page");
   is(contentWindow.TabItems.getItems().length, 1,
      "The total number of tab items is 1 after leaving the page");
 
-  let location = gBrowser.browsers[0].contentWindow.location.toString();
+  let location = gBrowser.browsers[0].currentURI.spec;
   is(location, "about:blank", "The open tab is the expected one");
 
   isnot(contentWindow.GroupItems.getActiveGroupItem(), activeGroup,
      "Active group is no longer the same");
 
   is(contentWindow.GroupItems.groupItems.length, 1,
      "Only one group is open");
 
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -340,82 +340,82 @@ Highlighter.prototype = {
   /**
    * Highlight this.node, unhilighting first if necessary.
    *
    * @param boolean aScroll
    *        Boolean determining whether to scroll or not.
    */
   highlight: function Highlighter_highlight(aScroll)
   {
-    // node is not set or node is not highlightable, bail
-    if (!this.node || !this.isNodeHighlightable(this.node)) {
-      return;
-    }
-
-    if (aScroll) {
-      this.node.scrollIntoView();
-    }
-
-    let clientRect = this.node.getBoundingClientRect();
+    let rect = null;
 
-    // Go up in the tree of frames to determine the correct rectangle.
-    // clientRect is read-only, we need to be able to change properties.
-    let rect = {top: clientRect.top,
-                left: clientRect.left,
-                width: clientRect.width,
-                height: clientRect.height};
+    if (this.node && this.isNodeHighlightable(this.node)) {
 
-    let frameWin = this.node.ownerDocument.defaultView;
-
-    // We iterate through all the parent windows.
-    while (true) {
-
-      // Does the selection overflow on the right of its window?
-      let diffx = frameWin.innerWidth - (rect.left + rect.width);
-      if (diffx < 0) {
-        rect.width += diffx;
+      if (aScroll) {
+        this.node.scrollIntoView();
       }
 
-      // Does the selection overflow on the bottom of its window?
-      let diffy = frameWin.innerHeight - (rect.top + rect.height);
-      if (diffy < 0) {
-        rect.height += diffy;
-      }
+      let clientRect = this.node.getBoundingClientRect();
+
+      // Go up in the tree of frames to determine the correct rectangle.
+      // clientRect is read-only, we need to be able to change properties.
+      rect = {top: clientRect.top,
+              left: clientRect.left,
+              width: clientRect.width,
+              height: clientRect.height};
+
+      let frameWin = this.node.ownerDocument.defaultView;
 
-      // Does the selection overflow on the left of its window?
-      if (rect.left < 0) {
-        rect.width += rect.left;
-        rect.left = 0;
-      }
+      // We iterate through all the parent windows.
+      while (true) {
 
-      // Does the selection overflow on the top of its window?
-      if (rect.top < 0) {
-        rect.height += rect.top;
-        rect.top = 0;
-      }
+        // Does the selection overflow on the right of its window?
+        let diffx = frameWin.innerWidth - (rect.left + rect.width);
+        if (diffx < 0) {
+          rect.width += diffx;
+        }
+
+        // Does the selection overflow on the bottom of its window?
+        let diffy = frameWin.innerHeight - (rect.top + rect.height);
+        if (diffy < 0) {
+          rect.height += diffy;
+        }
 
-      // Selection has been clipped to fit in its own window.
+        // Does the selection overflow on the left of its window?
+        if (rect.left < 0) {
+          rect.width += rect.left;
+          rect.left = 0;
+        }
 
-      // Are we in the top-level window?
-      if (frameWin.parent === frameWin || !frameWin.frameElement) {
-        break;
-      }
+        // Does the selection overflow on the top of its window?
+        if (rect.top < 0) {
+          rect.height += rect.top;
+          rect.top = 0;
+        }
+
+        // Selection has been clipped to fit in its own window.
 
-      // We are in an iframe.
-      // We take into account the parent iframe position and its
-      // offset (borders and padding).
-      let frameRect = frameWin.frameElement.getBoundingClientRect();
+        // Are we in the top-level window?
+        if (frameWin.parent === frameWin || !frameWin.frameElement) {
+          break;
+        }
 
-      let [offsetTop, offsetLeft] =
-        this.IUI.getIframeContentOffset(frameWin.frameElement);
+        // We are in an iframe.
+        // We take into account the parent iframe position and its
+        // offset (borders and padding).
+        let frameRect = frameWin.frameElement.getBoundingClientRect();
 
-      rect.top += frameRect.top + offsetTop;
-      rect.left += frameRect.left + offsetLeft;
+        let [offsetTop, offsetLeft] =
+          this.IUI.getIframeContentOffset(frameWin.frameElement);
 
-      frameWin = frameWin.parent;
+        rect.top += frameRect.top + offsetTop;
+        rect.left += frameRect.left + offsetLeft;
+
+        frameWin = frameWin.parent;
+      }
     }
 
     this.highlightRectangle(rect);
 
     this.moveInfobar();
 
     if (this._highlighting) {
       Services.obs.notifyObservers(null,
@@ -443,16 +443,21 @@ Highlighter.prototype = {
    *
    * @param object aRect
    *        The rectangle region to highlight.
    * @returns boolean
    *          True if the rectangle was highlighted, false otherwise.
    */
   highlightRectangle: function Highlighter_highlightRectangle(aRect)
   {
+    if (!aRect) {
+      this.unhighlight();
+      return;
+    }
+
     let oldRect = this._contentRect;
 
     if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
         aRect.width == oldRect.width && aRect.height == oldRect.height) {
       return this._highlighting; // same rectangle
     }
 
     // get page zoom factor, if any
@@ -464,16 +469,19 @@ Highlighter.prototype = {
     // adjust rect for zoom scaling
     let aRectScaled = {};
     for (let prop in aRect) {
       aRectScaled[prop] = aRect[prop] * zoom;
     }
 
     if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
         aRectScaled.width > 0 && aRectScaled.height > 0) {
+
+      this.veilTransparentBox.style.visibility = "visible";
+
       // The bottom div and the right div are flexibles (flex=1).
       // We don't need to resize them.
       this.veilTopBox.style.height = aRectScaled.top + "px";
       this.veilLeftBox.style.width = aRectScaled.left + "px";
       this.veilMiddleBox.style.height = aRectScaled.height + "px";
       this.veilTransparentBox.style.width = aRectScaled.width + "px";
 
       this._highlighting = true;
@@ -490,16 +498,17 @@ Highlighter.prototype = {
   /**
    * Clear the highlighter surface.
    */
   unhighlight: function Highlighter_unhighlight()
   {
     this._highlighting = false;
     this.veilMiddleBox.style.height = 0;
     this.veilTransparentBox.style.width = 0;
+    this.veilTransparentBox.style.visibility = "hidden";
     Services.obs.notifyObservers(null,
       INSPECTOR_NOTIFICATIONS.UNHIGHLIGHTING, null);
   },
 
   /**
    * Update node information (tagName#id.class) 
    */
   updateInfobar: function Highlighter_updateInfobar()
@@ -832,17 +841,17 @@ InspectorUI.prototype = {
     // InspectorUI is already up and running. Lock a node if asked (via context).
     if (this.isInspectorOpen && aNode) {
       this.inspectNode(aNode);
       this.stopInspecting();
       return;
     }
 
     // Observer used to inspect the specified element from content after the
-    // inspector UI has been opened.
+    // inspector UI has been opened (via the content context menu).
     function inspectObserver(aElement) {
       Services.obs.removeObserver(boundInspectObserver,
                                   INSPECTOR_NOTIFICATIONS.OPENED,
                                   false);
       this.inspectNode(aElement);
       this.stopInspecting();
     };
 
@@ -865,52 +874,38 @@ InspectorUI.prototype = {
 
     this.initTools();
 
     if (!this.TreePanel && this.treePanelEnabled) {
       Cu.import("resource:///modules/TreePanel.jsm", this);
       this.treePanel = new this.TreePanel(this.chromeWin, this);
     }
 
+    if (Services.prefs.getBoolPref("devtools.styleinspector.enabled") &&
+        !this.toolRegistered("styleinspector")) {
+      this.stylePanel = new StyleInspector(this.chromeWin, this);
+    }
+
     this.toolbar.hidden = false;
     this.inspectMenuitem.setAttribute("checked", true);
 
     this.isDirty = false;
 
     this.progressListener = new InspectorProgressListener(this);
 
     // initialize the highlighter
     this.initializeHighlighter();
   },
 
   /**
    * Register and initialize any included tools.
    */
   initTools: function IUI_initTools()
   {
-    // Style inspector
-    if (Services.prefs.getBoolPref("devtools.styleinspector.enabled") &&
-        !this.toolRegistered("styleinspector")) {
-      let stylePanel = StyleInspector.createPanel(true);
-      this.registerTool({
-        id: "styleinspector",
-        label: StyleInspector.l10n("style.highlighter.button.label"),
-        tooltiptext: StyleInspector.l10n("style.highlighter.button.tooltip"),
-        accesskey: StyleInspector.l10n("style.highlighter.accesskey"),
-        context: stylePanel,
-        get isOpen() stylePanel.isOpen(),
-        onSelect: stylePanel.selectNode,
-        show: stylePanel.showTool,
-        hide: stylePanel.hideTool,
-        dim: stylePanel.dimTool,
-        panel: stylePanel,
-        unregister: stylePanel.destroy,
-      });
-      this.stylePanel = stylePanel;
-    }
+    // Extras go here.
   },
 
   /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
     this.highlighter = new Highlighter(this);
--- a/browser/devtools/highlighter/test/browser_inspector_initialization.js
+++ b/browser/devtools/highlighter/test/browser_inspector_initialization.js
@@ -88,17 +88,17 @@ function treePanelTests()
   Services.obs.removeObserver(treePanelTests,
     InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
   Services.obs.addObserver(stylePanelTests,
     "StyleInspector-opened", false);
 
   ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
 
   executeSoon(function() {
-    InspectorUI.stylePanel.showTool(doc.body);
+    InspectorUI.stylePanel.open(doc.body);
   });
 }
 
 function stylePanelTests()
 {
   Services.obs.removeObserver(stylePanelTests, "StyleInspector-opened");
   Services.obs.addObserver(runContextMenuTest,
     InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm
+++ b/browser/devtools/styleinspector/CssHtmlTree.jsm
@@ -51,48 +51,48 @@ Cu.import("resource:///modules/devtools/
 
 var EXPORTED_SYMBOLS = ["CssHtmlTree", "PropertyView"];
 
 /**
  * CssHtmlTree is a panel that manages the display of a table sorted by style.
  * There should be one instance of CssHtmlTree per style display (of which there
  * will generally only be one).
  *
- * @params {Document} aStyleWin The main XUL browser document
- * @params {CssLogic} aCssLogic How we dig into the CSS. See CssLogic.jsm
+ * @params {StyleInspector} aStyleInspector The owner of this CssHtmlTree
  * @constructor
  */
-function CssHtmlTree(aStyleWin, aCssLogic, aPanel)
+function CssHtmlTree(aStyleInspector)
 {
-  this.styleWin = aStyleWin;
-  this.cssLogic = aCssLogic;
-  this.doc = aPanel.ownerDocument;
-  this.win = this.doc.defaultView;
-  this.getRTLAttr = CssHtmlTree.getRTLAttr;
-  this.propertyViews = {};
+  this.styleWin = aStyleInspector.iframe;
+  this.styleInspector = aStyleInspector;
+  this.cssLogic = aStyleInspector.cssLogic;
+  this.doc = aStyleInspector.document;
+  this.win = aStyleInspector.window;
+  this.getRTLAttr = this.win.getComputedStyle(this.win.gBrowser).direction;
+  this.propertyViews = [];
 
   // The document in which we display the results (csshtmltree.xhtml).
   this.styleDocument = this.styleWin.contentWindow.document;
 
   // Nodes used in templating
   this.root = this.styleDocument.getElementById("root");
   this.path = this.styleDocument.getElementById("path");
   this.templateRoot = this.styleDocument.getElementById("templateRoot");
   this.templatePath = this.styleDocument.getElementById("templatePath");
   this.propertyContainer = this.styleDocument.getElementById("propertyContainer");
   this.templateProperty = this.styleDocument.getElementById("templateProperty");
-  this.panel = aPanel;
+  this.panel = aStyleInspector.panel;
 
   // The element that we're inspecting, and the document that it comes from.
   this.viewedElement = null;
   this.createStyleViews();
 }
 
 /**
- * Memonized lookup of a l10n string from a string bundle.
+ * Memoized lookup of a l10n string from a string bundle.
  * @param {string} aName The key to lookup.
  * @returns A localized version of the given key.
  */
 CssHtmlTree.l10n = function CssHtmlTree_l10n(aName)
 {
   try {
     return CssHtmlTree._strings.GetStringFromName(aName);
   } catch (ex) {
@@ -124,49 +124,34 @@ CssHtmlTree.processTemplate = function C
   // values, so we need to clone the template first.
   let duplicated = aTemplate.cloneNode(true);
   new Templater().processNode(duplicated, aData);
   while (duplicated.firstChild) {
     aDestination.appendChild(duplicated.firstChild);
   }
 };
 
-/**
- * Checks whether the UI is RTL
- * @return {Boolean} true or false
- */
-CssHtmlTree.isRTL = function CssHtmlTree_isRTL()
-{
-  return CssHtmlTree.getRTLAttr == "rtl";
-};
-
-/**
- * Checks whether the UI is RTL
- * @return {String} "ltr" or "rtl"
- */
-XPCOMUtils.defineLazyGetter(CssHtmlTree, "getRTLAttr", function() {
-  let mainWindow = Services.wm.getMostRecentWindow("navigator:browser");
-  return mainWindow.getComputedStyle(mainWindow.gBrowser).direction;
-});
-
 XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() Services.strings
     .createBundle("chrome://browser/locale/styleinspector.properties"));
 
 CssHtmlTree.prototype = {
   htmlComplete: false,
 
   // Used for cancelling timeouts in the style filter.
   filterChangedTimeout: null,
 
   // The search filter
   searchField: null,
   
   // Reference to the "Only user Styles" checkbox.
   onlyUserStylesCheckbox: null,
 
+  // Holds the ID of the panelRefresh timeout.
+  _panelRefreshTimeout: null,
+
   get showOnlyUserStyles()
   {
     return this.onlyUserStylesCheckbox.checked;
   },
 
   /**
    * Update the highlighted element. The CssHtmlTree panel will show the style
    * information for the given element.
@@ -188,26 +173,26 @@ CssHtmlTree.prototype = {
       CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
 
       // We use a setTimeout loop to display the properties in batches of 15 at a
       // time. This results in a perceptibly more responsive UI.
       let i = 0;
       let batchSize = 15;
       let max = CssHtmlTree.propertyNames.length - 1;
       function displayProperties() {
-        if (this.viewedElement == aElement && this.panel.isOpen()) {
+        if (this.viewedElement == aElement && this.styleInspector.isOpen()) {
           // Display the next 15 properties
           for (let step = i + batchSize; i < step && i <= max; i++) {
             let name = CssHtmlTree.propertyNames[i];
             let propView = new PropertyView(this, name);
             CssHtmlTree.processTemplate(this.templateProperty,
               this.propertyContainer, propView, true);
             propView.refreshMatchedSelectors();
             propView.refreshUnmatchedSelectors();
-            this.propertyViews[name] = propView;
+            this.propertyViews.push(propView);
           }
           if (i < max) {
             // There are still some properties to display. We loop here to display
             // the next batch of 15.
             this.win.setTimeout(displayProperties.bind(this), 50);
           } else {
             this.htmlComplete = true;
             Services.obs.notifyObservers(null, "StyleInspector-populated", null);
@@ -218,41 +203,50 @@ CssHtmlTree.prototype = {
     }
   },
 
   /**
    * Refresh the panel content.
    */
   refreshPanel: function CssHtmlTree_refreshPanel()
   {
-    for each(let propView in this.propertyViews) {
-      propView.refresh();
+    this.win.clearTimeout(this._panelRefreshTimeout);
+
+    // We use a setTimeout loop to display the properties in batches of 15 at a
+    // time. This results in a perceptibly more responsive UI.
+    let i = 0;
+    let batchSize = 15;
+    let max = this.propertyViews.length - 1;
+    function refreshView() {
+      // Refresh the next 15 property views
+      for (let step = i + batchSize; i < step && i <= max; i++) {
+        this.propertyViews[i].refresh();
+      }
+      if (i < max) {
+        // There are still some property views to refresh. We loop here to
+        // display the next batch of 15.
+        this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 0);
+      } else {
+        Services.obs.notifyObservers(null, "StyleInspector-populated", null);
+      }
     }
-    Services.obs.notifyObservers(null, "StyleInspector-populated", null);
+    this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 0);
   },
 
   /**
    * Called when the user clicks on a parent element in the "current element"
    * path.
    *
    * @param {Event} aEvent the DOM Event object.
    */
   pathClick: function CssHtmlTree_pathClick(aEvent)
   {
     aEvent.preventDefault();
     if (aEvent.target && this.viewedElement != aEvent.target.pathElement) {
-      if (this.win.InspectorUI.selection) {
-        if (aEvent.target.pathElement != this.win.InspectorUI.selection) {
-          let elt = aEvent.target.pathElement;
-          this.win.InspectorUI.inspectNode(elt);
-          this.panel.selectNode(elt);
-        }
-      } else {
-        this.panel.selectNode(aEvent.target.pathElement);
-      }
+      this.styleInspector.selectFromPath(aEvent.target.pathElement);
     }
   },
 
   /**
    * Called when the user enters a search term.
    *
    * @param {Event} aEvent the DOM Event object.
    */
@@ -344,37 +338,37 @@ CssHtmlTree.prototype = {
     delete this.templateProperty;
     delete this.panel;
 
     // The document in which we display the results (csshtmltree.xhtml).
     delete this.styleDocument;
 
     // The element that we're inspecting, and the document that it comes from.
     delete this.propertyViews;
-    delete this.getRTLAttr;
     delete this.styleWin;
     delete this.cssLogic;
     delete this.doc;
     delete this.win;
+    delete this.styleInspector;
   },
 };
 
 /**
  * A container to give easy access to property data from the template engine.
  *
  * @constructor
  * @param {CssHtmlTree} aTree the CssHtmlTree instance we are working with.
  * @param {string} aName the CSS property name for which this PropertyView
  * instance will render the rules.
  */
 function PropertyView(aTree, aName)
 {
   this.tree = aTree;
   this.name = aName;
-  this.getRTLAttr = CssHtmlTree.getRTLAttr;
+  this.getRTLAttr = aTree.getRTLAttr;
 
   this.link = "https://developer.mozilla.org/en/CSS/" + aName;
 
   this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors");
   this.templateUnmatchedSelectors = aTree.styleDocument.getElementById("templateUnmatchedSelectors");
 }
 
 PropertyView.prototype = {
@@ -555,34 +549,34 @@ PropertyView.prototype = {
    * displaying.
    */
   get matchedSelectorViews()
   {
     if (!this._matchedSelectorViews) {
       this._matchedSelectorViews = [];
       this.propertyInfo.matchedSelectors.forEach(
         function matchedSelectorViews_convert(aSelectorInfo) {
-          this._matchedSelectorViews.push(new SelectorView(aSelectorInfo));
+          this._matchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
         }, this);
     }
 
     return this._matchedSelectorViews;
   },
 
     /**
    * Provide access to the unmatched SelectorViews that we are currently
    * displaying.
    */
   get unmatchedSelectorViews()
   {
     if (!this._unmatchedSelectorViews) {
       this._unmatchedSelectorViews = [];
       this.propertyInfo.unmatchedSelectors.forEach(
         function unmatchedSelectorViews_convert(aSelectorInfo) {
-          this._unmatchedSelectorViews.push(new SelectorView(aSelectorInfo));
+          this._unmatchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
         }, this);
     }
 
     return this._unmatchedSelectorViews;
   },
 
   /**
    * The action when a user expands matched selectors.
@@ -602,19 +596,22 @@ PropertyView.prototype = {
     this.unmatchedExpanded = !this.unmatchedExpanded;
     this.refreshUnmatchedSelectors();
     aEvent.preventDefault();
   },
 };
 
 /**
  * A container to view us easy access to display data from a CssRule
+ * @param CssHtmlTree aTree, the owning CssHtmlTree
+ * @param aSelectorInfo
  */
-function SelectorView(aSelectorInfo)
+function SelectorView(aTree, aSelectorInfo)
 {
+  this.tree = aTree;
   this.selectorInfo = aSelectorInfo;
   this._cacheStatusNames();
 }
 
 /**
  * Decode for cssInfo.rule.status
  * @see SelectorView.prototype._cacheStatusNames
  * @see CssLogic.STATUS
@@ -669,34 +666,37 @@ SelectorView.prototype = {
     return SelectorView.CLASS_NAMES[this.selectorInfo.status];
   },
 
   /**
    * A localized Get localized human readable info
    */
   humanReadableText: function SelectorView_humanReadableText(aElement)
   {
-    if (CssHtmlTree.isRTL()) {
+    if (this.tree.getRTLAttr == "rtl") {
       return this.selectorInfo.value + " \u2190 " + this.text(aElement);
     } else {
       return this.text(aElement) + " \u2192 " + this.selectorInfo.value;
     }
   },
 
   text: function SelectorView_text(aElement) {
     let result = this.selectorInfo.selector.text;
     if (this.selectorInfo.elementStyle) {
-      if (this.selectorInfo.sourceElement == this.win.InspectorUI.selection) {
-        result = "this";
-      } else {
-        result = CssLogic.getShortName(this.selectorInfo.sourceElement);
-        aElement.parentNode.querySelector(".rule-link > a").
-          addEventListener("click", function(aEvent) {
-            this.win.InspectorUI.inspectNode(this.selectorInfo.sourceElement);
-            aEvent.preventDefault();
-          }, false);
+      if (this.tree.styleInspector.IUI) {
+        if (this.selectorInfo.sourceElement == this.tree.styleInspector.IUI.selection)
+        {
+          result = "this";
+        } else {
+          result = CssLogic.getShortName(this.selectorInfo.sourceElement);
+        }
       }
-
+      aElement.parentNode.querySelector(".rule-link > a").
+        addEventListener("click", function(aEvent) {
+          this.tree.styleInspector.selectFromPath(this.selectorInfo.sourceElement);
+          aEvent.preventDefault();
+        }.bind(this), false);
       result += ".style";
     }
+
     return result;
   },
 };
--- a/browser/devtools/styleinspector/StyleInspector.jsm
+++ b/browser/devtools/styleinspector/StyleInspector.jsm
@@ -41,204 +41,265 @@
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var EXPORTED_SYMBOLS = ["StyleInspector"];
 
-var StyleInspector = {
+/**
+ * StyleInspector Constructor Function.
+ * @param {window} aContext, the chrome window context we're calling from.
+ * @param {InspectorUI} aIUI (optional) An InspectorUI instance if called from the
+ *        Highlighter.
+ */
+function StyleInspector(aContext, aIUI)
+{
+  this._init(aContext, aIUI);
+};
+
+StyleInspector.prototype = {
+
   /**
-   * Is the Style Inspector enabled?
-   * @returns {Boolean} true or false
+   * Initialization method called from constructor.
+   * @param {window} aContext, the chrome window context we're calling from.
+   * @param {InspectorUI} aIUI (optional) An InspectorUI instance if called from
+   *        the Highlighter.
    */
-  get isEnabled()
+  _init: function SI__init(aContext, aIUI)
   {
-    return Services.prefs.getBoolPref("devtools.styleinspector.enabled");
+    this.window = aContext;
+    this.IUI = aIUI;
+    this.document = this.window.document;
+    this.cssLogic = new CssLogic();
+    this.panelReady = false;
+    this.iframeReady = false;
+
+    // Were we invoked from the Highlighter?
+    if (this.IUI) {
+      this.createPanel(true);
+
+      let isOpen = this.isOpen.bind(this);
+
+      this.registrationObject = {
+        id: "styleinspector",
+        label: this.l10n("style.highlighter.button.label"),
+        tooltiptext: this.l10n("style.highlighter.button.tooltip"),
+        accesskey: this.l10n("style.highlighter.accesskey"),
+        context: this,
+        get isOpen() isOpen(),
+        onSelect: this.selectNode,
+        show: this.open,
+        hide: this.close,
+        dim: this.dimTool,
+        panel: this.panel,
+        unregister: this.destroy
+      };
+
+      // Register the registrationObject with the Highlighter
+      this.IUI.registerTool(this.registrationObject);
+    }
   },
 
   /**
    * Factory method to create the actual style panel
    * @param {Boolean} aPreserveOnHide Prevents destroy from being called
    * onpopuphide. USE WITH CAUTION: When this value is set to true then you are
    * responsible to manually call destroy from outside the style inspector.
+   * @param {function} aCallback (optional) callback to fire when ready.
    */
-  createPanel: function SI_createPanel(aPreserveOnHide)
+  createPanel: function SI_createPanel(aPreserveOnHide, aCallback)
   {
-    let win = Services.wm.getMostRecentWindow("navigator:browser");
-    let popupSet = win.document.getElementById("mainPopupSet");
-    let panel = win.document.createElement("panel");
+    let popupSet = this.document.getElementById("mainPopupSet");
+    let panel = this.document.createElement("panel");
+    this.preserveOnHide = !!aPreserveOnHide;
 
     panel.setAttribute("class", "styleInspector");
     panel.setAttribute("orient", "vertical");
     panel.setAttribute("ignorekeys", "true");
     panel.setAttribute("noautofocus", "true");
     panel.setAttribute("noautohide", "true");
     panel.setAttribute("titlebar", "normal");
     panel.setAttribute("close", "true");
-    panel.setAttribute("label", StyleInspector.l10n("panelTitle"));
+    panel.setAttribute("label", this.l10n("panelTitle"));
     panel.setAttribute("width", 350);
-    panel.setAttribute("height", win.screen.height / 2);
+    panel.setAttribute("height", this.window.screen.height / 2);
 
-    let vbox = win.document.createElement("vbox");
+    let vbox = this.document.createElement("vbox");
     vbox.setAttribute("flex", "1");
     panel.appendChild(vbox);
 
-    let iframe = win.document.createElement("iframe");
+    let iframe = this.document.createElement("iframe");
+    let boundIframeOnLoad = function loadedInitializeIframe()
+    {
+      this.iframe.removeEventListener("load", boundIframeOnLoad, true);
+      this.iframeReady = true;
+      if (aCallback)
+        aCallback(this);
+    }.bind(this);
+
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("tooltip", "aHTMLTooltip");
+    iframe.addEventListener("load", boundIframeOnLoad, true);
     iframe.setAttribute("src", "chrome://browser/content/csshtmltree.xhtml");
-    iframe.addEventListener("load", SI_iframeOnload, true);
+
     vbox.appendChild(iframe);
 
-    let hbox = win.document.createElement("hbox");
+    let hbox = this.document.createElement("hbox");
     hbox.setAttribute("class", "resizerbox");
     vbox.appendChild(hbox);
 
-    let spacer = win.document.createElement("spacer");
+    let spacer = this.document.createElement("spacer");
     spacer.setAttribute("flex", "1");
     hbox.appendChild(spacer);
 
-    let resizer = win.document.createElement("resizer");
+    let resizer = this.document.createElement("resizer");
     resizer.setAttribute("dir", "bottomend");
     hbox.appendChild(resizer);
     popupSet.appendChild(panel);
 
-    /**
-     * Iframe's onload event
-     */
-    let iframeReady = false;
-    function SI_iframeOnload() {
-      iframe.removeEventListener("load", SI_iframeOnload, true);
-      iframeReady = true;
-      if (panelReady) {
-        SI_popupShown.call(panel);
-      }
+    this._boundPopupShown = this.popupShown.bind(this);
+    this._boundPopupHidden = this.popupHidden.bind(this);
+    panel.addEventListener("popupshown", this._boundPopupShown, false);
+    panel.addEventListener("popuphidden", this._boundPopupHidden, false);
+
+    this.panel = panel;
+    this.iframe = iframe;
+
+    return panel;
+  },
+
+  /**
+   * Event handler for the popupshown event.
+   */
+  popupShown: function SI_popupShown()
+  {
+    this.panelReady = true;
+    if (this.iframeReady) {
+      this.cssHtmlTree = new CssHtmlTree(this);
+      let selectedNode = this.selectedNode || null;
+      this.cssLogic.highlight(selectedNode);
+      this.cssHtmlTree.highlight(selectedNode);
+      Services.obs.notifyObservers(null, "StyleInspector-opened", null);
     }
+  },
+
+  /**
+   * Event handler for the popuphidden event.
+   * Hide the popup and conditionally destroy it
+   */
+  popupHidden: function SI_popupHidden()
+  {
+    if (this.preserveOnHide) {
+      Services.obs.notifyObservers(null, "StyleInspector-closed", null);
+    } else {
+      this.destroy();
+    }
+  },
 
-    /**
-     * Initialize the popup when it is first shown
-     */
-    let panelReady = false;
-    function SI_popupShown() {
-      panelReady = true;
-      if (iframeReady) {
-        if (!this.cssLogic) {
-          this.cssLogic = new CssLogic();
-          this.cssHtmlTree = new CssHtmlTree(iframe, this.cssLogic, this);
-        }
-        let selectedNode = this.selectedNode || null;
-        this.cssLogic.highlight(selectedNode);
-        this.cssHtmlTree.highlight(selectedNode);
-        Services.obs.notifyObservers(null, "StyleInspector-opened", null);
+  /**
+   * Check if the style inspector is open.
+   * @returns boolean
+   */
+  isOpen: function SI_isOpen()
+  {
+    return this.panel && this.panel.state && this.panel.state == "open";
+  },
+
+  /**
+   * Select from Path (via CssHtmlTree_pathClick)
+   * @param aNode The node to inspect.
+   */
+  selectFromPath: function SI_selectFromPath(aNode)
+  {
+    if (this.IUI && this.IUI.selection) {
+      if (aNode != this.IUI.selection) {
+        this.IUI.inspectNode(aNode);
       }
+    } else {
+      this.selectNode(aNode);
     }
+  },
 
-    /**
-     * Hide the popup and conditionally destroy it
-     */
-    function SI_popupHidden() {
-      if (panel.preserveOnHide) {
-        Services.obs.notifyObservers(null, "StyleInspector-closed", null);
-      } else {
-        panel.destroy();
-      }
+  /**
+   * Select a node to inspect in the Style Inspector panel
+   * @param aNode The node to inspect.
+   */
+  selectNode: function SI_selectNode(aNode)
+  {
+    this.selectedNode = aNode;
+    if (this.isOpen() && !this.panel.hasAttribute("dimmed")) {
+      this.cssLogic.highlight(aNode);
+      this.cssHtmlTree.highlight(aNode);
+    }
+  },
+
+  /**
+   * Destroy the style panel, remove listeners etc.
+   */
+  destroy: function SI_destroy()
+  {
+    if (this.isOpen())
+      this.close();
+    if (this.cssHtmlTree)
+      this.cssHtmlTree.destroy();
+    if (this.iframe) {
+      this.iframe.parentNode.removeChild(this.iframe);
+      delete this.iframe;
     }
 
-    panel.addEventListener("popupshown", SI_popupShown, false);
-    panel.addEventListener("popuphidden", SI_popupHidden, false);
-    panel.preserveOnHide = !!aPreserveOnHide;
-
-    /**
-     * Check if the style inspector is open
-     */
-    panel.isOpen = function SI_isOpen()
-    {
-      return this.state && this.state == "open";
-    };
+    delete this.cssLogic;
+    delete this.cssHtmlTree;
+    this.panel.removeEventListener("popupshown", this._boundPopupShown, false);
+    this.panel.removeEventListener("popuphidden", this._boundPopupHidden, false);
+    delete this._boundPopupShown;
+    delete this._boundPopupHidden;
+    this.panel.parentNode.removeChild(this.panel);
+    delete this.panel;
+    delete this.doc;
+    delete this.win;
+    delete CssHtmlTree.win;
+    Services.obs.notifyObservers(null, "StyleInspector-closed", null);
+  },
 
-    /**
-     * Select a node to inspect in the Style Inspector panel
-     *
-     * @param aNode The node to inspect
-     */
-    panel.selectNode = function SI_selectNode(aNode)
-    {
-      this.selectedNode = aNode;
-      if (this.isOpen() && !this.hasAttribute("dimmed")) {
-        this.cssLogic.highlight(aNode);
-        this.cssHtmlTree.highlight(aNode);
-      }
-    };
-
-    /**
-     * Destroy the style panel, remove listeners etc.
-     */
-    panel.destroy = function SI_destroy()
-    {
-      if (this.isOpen())
-        this.hideTool();
-      if (panel.cssHtmlTree)
-        panel.cssHtmlTree.destroy();
-      if (iframe) {
-        iframe.parentNode.removeChild(iframe);
-        iframe = null;
-      }
+  /**
+   * Dim or undim a panel by setting or removing a dimmed attribute.
+   * @param aState
+   *        true = dim, false = undim
+   */
+  dimTool: function SI_dimTool(aState)
+  {
+    if (!this.isOpen())
+      return;
 
-      delete panel.cssLogic;
-      delete panel.cssHtmlTree;
-      panel.removeEventListener("popupshown", SI_popupShown, false);
-      panel.removeEventListener("popuphidden", SI_popupHidden, false);
-      panel.parentNode.removeChild(panel);
-      panel = null;
-      Services.obs.notifyObservers(null, "StyleInspector-closed", null);
-    };
-
-    /**
-     * Dim or undim a panel by setting or removing a dimmed attribute.
-     *
-     * @param aState
-     *        true = dim, false = undim
-     */
-    panel.dimTool = function SI_dimTool(aState)
-    {
-      if (!this.isOpen())
-        return;
+    if (aState) {
+      this.panel.setAttribute("dimmed", "true");
+    } else if (this.panel.hasAttribute("dimmed")) {
+      this.panel.removeAttribute("dimmed");
+    }
+  },
 
-      if (aState) {
-        this.setAttribute("dimmed", "true");
-      } else if (this.hasAttribute("dimmed")) {
-        this.removeAttribute("dimmed");
-      }
-    };
-
-    panel.showTool = function SI_showTool(aSelection)
-    {
-      this.selectNode(aSelection);
-      let win = Services.wm.getMostRecentWindow("navigator:browser");
-      this.openPopup(win.gBrowser.selectedBrowser, "end_before", 0, 0,
-        false, false);
-    };
+  /**
+   * Open the panel.
+   * @param {DOMNode} aSelection the (optional) DOM node to select.
+   */
+  open: function SI_open(aSelection)
+  {
+    this.selectNode(aSelection);
+    this.panel.openPopup(this.window.gBrowser.selectedBrowser, "end_before", 0, 0,
+      false, false);
+  },
 
-    panel.hideTool = function SI_hideTool()
-    {
-      this.hidePopup();
-    };
-
-    /**
-     * Is the Style Inspector initialized?
-     * @returns {Boolean} true or false
-     */
-    function isInitialized()
-    {
-      return panel.cssLogic && panel.cssHtmlTree;
-    }
-
-    return panel;
+  /**
+   * Close the panel.
+   */
+  close: function SI_close()
+  {
+    this.panel.hidePopup();
   },
 
   /**
    * Memonized lookup of a l10n string from a string bundle.
    * @param {string} aName The key to lookup.
    * @returns A localized version of the given key.
    */
   l10n: function SI_l10n(aName)
--- a/browser/devtools/styleinspector/test/browser/browser_bug683672.js
+++ b/browser/devtools/styleinspector/test/browser/browser_bug683672.js
@@ -16,35 +16,38 @@ function test()
   waitForExplicitFinish();
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
 function tabLoaded()
 {
   browser.removeEventListener("load", tabLoaded, true);
+  doc = content.document;
   ok(window.StyleInspector, "StyleInspector exists");
-  ok(StyleInspector.isEnabled, "style inspector preference is enabled");
-  stylePanel = StyleInspector.createPanel();
+  // ok(StyleInspector.isEnabled, "style inspector preference is enabled");
+  stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runTests, "StyleInspector-opened", false);
-  stylePanel.openPopup();
+  stylePanel.createPanel(false, function() {
+    stylePanel.open(doc.body);
+  });
 }
 
 function runTests()
 {
   Services.obs.removeObserver(runTests, "StyleInspector-opened", false);
 
   ok(stylePanel.isOpen(), "style inspector is open");
 
   testMatchedSelectors();
   testUnmatchedSelectors();
 
   info("finishing up");
   Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
-  stylePanel.hidePopup();
+  stylePanel.close();
 }
 
 function testMatchedSelectors()
 {
   info("checking selector counts, matched rules and titles");
   let div = content.document.getElementById("test");
   ok(div, "captain, we have the div");
 
--- a/browser/devtools/styleinspector/test/browser/browser_styleinspector.js
+++ b/browser/devtools/styleinspector/test/browser/browser_styleinspector.js
@@ -22,20 +22,21 @@ function createDocument()
     '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' +
     '<p>Inspect using inspectstyle(document.querySelectorAll("span")[0])</p>' +
     '</div>';
   doc.title = "Style Inspector Test";
   ok(window.StyleInspector, "StyleInspector exists");
-  ok(StyleInspector.isEnabled, "style inspector preference is enabled");
-  stylePanel = StyleInspector.createPanel();
+  stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
-  stylePanel.openPopup(gBrowser.selectedBrowser, "end_before", 0, 0, false, false);
+  stylePanel.createPanel(false, function() {
+    stylePanel.open(doc.body);
+  });
 }
 
 function runStyleInspectorTests()
 {
   Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-opened", false);
 
   ok(stylePanel.isOpen(), "style inspector is open");
 
@@ -50,17 +51,17 @@ function runStyleInspectorTests()
     is(spans[i], htmlTree.viewedElement,
       "style inspector node matches the selected node");
     is(htmlTree.viewedElement, stylePanel.cssLogic.viewedElement,
        "cssLogic node matches the cssHtmlTree node");
   }
 
   SI_CheckProperty();
   Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
-  stylePanel.hidePopup();
+  stylePanel.close();
 }
 
 function SI_CheckProperty()
 {
   let cssLogic = stylePanel.cssLogic;
   let propertyInfo = cssLogic.getPropertyInfo("color");
   ok(propertyInfo.matchedRuleCount > 0, "color property has matching rules");
   ok(propertyInfo.unmatchedRuleCount > 0, "color property has unmatched rules");
--- a/browser/devtools/styleinspector/test/browser/browser_styleinspector_bug_672744_search_filter.js
+++ b/browser/devtools/styleinspector/test/browser/browser_styleinspector_bug_672744_search_filter.js
@@ -10,20 +10,22 @@ let stylePanel;
 function createDocument()
 {
   doc.body.innerHTML = '<style type="text/css"> ' +
     '.matches {color: #F00;}</style>' +
     '<span id="matches" class="matches">Some styled text</span>' +
     '</div>';
   doc.title = "Style Inspector Search Filter Test";
   ok(window.StyleInspector, "StyleInspector exists");
-  ok(StyleInspector.isEnabled, "style inspector preference is enabled");
-  stylePanel = StyleInspector.createPanel();
+  // ok(StyleInspector.isEnabled, "style inspector preference is enabled");
+  stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
-  stylePanel.openPopup(gBrowser.selectedBrowser, "end_before", 0, 0, false, false);
+  stylePanel.createPanel(false, function() {
+    stylePanel.open(doc.body);
+  });
 }
 
 function runStyleInspectorTests()
 {
   Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-opened", false);
 
   ok(stylePanel.isOpen(), "style inspector is open");
 
@@ -45,27 +47,27 @@ function SI_inspectNode()
      "cssLogic node matches the cssHtmlTree node");
 }
 
 function SI_toggleDefaultStyles()
 {
   Services.obs.removeObserver(SI_toggleDefaultStyles, "StyleInspector-populated", false);
 
   info("clearing \"only user styles\" checkbox");
-  let iframe = stylePanel.querySelector("iframe");
+  let iframe = stylePanel.iframe;
   let checkbox = iframe.contentDocument.querySelector(".userStyles");
   Services.obs.addObserver(SI_AddFilterText, "StyleInspector-populated", false);
   EventUtils.synthesizeMouse(checkbox, 5, 5, {}, iframe.contentWindow);
 }
 
 function SI_AddFilterText()
 {
   Services.obs.removeObserver(SI_AddFilterText, "StyleInspector-populated", false);
 
-  let iframe = stylePanel.querySelector("iframe");
+  let iframe = stylePanel.iframe;
   let searchbar = iframe.contentDocument.querySelector(".searchfield");
 
   Services.obs.addObserver(SI_checkFilter, "StyleInspector-populated", false);
   info("setting filter text to \"color\"");
   searchbar.focus();
   EventUtils.synthesizeKey("c", {}, iframe.contentWindow);
   EventUtils.synthesizeKey("o", {}, iframe.contentWindow);
   EventUtils.synthesizeKey("l", {}, iframe.contentWindow);
@@ -74,24 +76,24 @@ function SI_AddFilterText()
 }
 
 function SI_checkFilter()
 {
   Services.obs.removeObserver(SI_checkFilter, "StyleInspector-populated", false);
   let propertyViews = stylePanel.cssHtmlTree.propertyViews;
 
   info("check that the correct properties are visible");
-  for each(let propView in propertyViews) {
+  propertyViews.forEach(function(propView) {
     let name = propView.name;
     is(propView.visible, name.indexOf("color") > -1,
       "span " + name + " property visibility check");
-  }
+  });
 
   Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
-  stylePanel.hidePopup();
+  stylePanel.close();
 }
 
 function finishUp()
 {
   Services.obs.removeObserver(finishUp, "StyleInspector-closed", false);
   ok(!stylePanel.isOpen(), "style inspector is closed");
   doc = stylePanel = null;
   gBrowser.removeCurrentTab();
--- a/browser/devtools/styleinspector/test/browser/browser_styleinspector_bug_672746_default_styles.js
+++ b/browser/devtools/styleinspector/test/browser/browser_styleinspector_bug_672746_default_styles.js
@@ -10,20 +10,22 @@ let stylePanel;
 function createDocument()
 {
   doc.body.innerHTML = '<style type="text/css"> ' +
     '.matches {color: #F00;}</style>' +
     '<span id="matches" class="matches">Some styled text</span>' +
     '</div>';
   doc.title = "Style Inspector Default Styles Test";
   ok(window.StyleInspector, "StyleInspector exists");
-  ok(StyleInspector.isEnabled, "style inspector preference is enabled");
-  stylePanel = StyleInspector.createPanel();
+  // ok(StyleInspector.isEnabled, "style inspector preference is enabled");
+  stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
-  stylePanel.openPopup(gBrowser.selectedBrowser, "end_before", 0, 0, false, false);
+  stylePanel.createPanel(false, function() {
+    stylePanel.open(doc.body);
+  });
 }
 
 function runStyleInspectorTests()
 {
   Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-opened", false);
 
   ok(stylePanel.isOpen(), "style inspector is open");
 
@@ -54,39 +56,44 @@ function SI_check()
     "span #matches background-color property is hidden");
 
   SI_toggleDefaultStyles();
 }
 
 function SI_toggleDefaultStyles()
 {
   // Click on the checkbox.
-  let iframe = stylePanel.querySelector("iframe");
+  let iframe = stylePanel.iframe;
   let checkbox = iframe.contentDocument.querySelector(".userStyles");
   Services.obs.addObserver(SI_checkDefaultStyles, "StyleInspector-populated", false);
   EventUtils.synthesizeMouse(checkbox, 5, 5, {}, iframe.contentWindow);
 }
 
 function SI_checkDefaultStyles()
 {
   Services.obs.removeObserver(SI_checkDefaultStyles, "StyleInspector-populated", false);
   // Check that the default styles are now applied.
   is(propertyVisible("color"), true,
       "span color property is visible");
   is(propertyVisible("background-color"), true,
       "span background-color property is visible");
 
   Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
-  stylePanel.hidePopup();
+  stylePanel.close();
 }
 
 function propertyVisible(aName)
 {
+  info("Checking property visibility for " + aName);
   let propertyViews = stylePanel.cssHtmlTree.propertyViews;
-  return propertyViews[aName].className == "property-view";
+  for each (let propView in propertyViews) {
+    if (propView.name == aName) {
+      return propView.className == "property-view";
+    }
+  }
 }
 
 function finishUp()
 {
   Services.obs.removeObserver(finishUp, "StyleInspector-closed", false);
   ok(!stylePanel.isOpen(), "style inspector is closed");
   doc = stylePanel = null;
   gBrowser.removeCurrentTab();
--- a/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js
+++ b/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js
@@ -110,18 +110,20 @@ function teststylePanels() {
   // 3. #container
   //
   // We will loop through each instance and check that the correct node is
   // selected and that the correct css selector has been selected as active
   info("looping through array to check initialization");
   for (let i = 0, max = stylePanels.length; i < max; i++) {
     ok(stylePanels[i], "style inspector instance " + i +
        " correctly initialized");
-    ok(stylePanels[i].isOpen(), "style inspector " + i + " is open");
+    is(stylePanels[i].state, "open", "style inspector " + i + " is open");
 
+/*  // the following should be tested elsewhere
+    // TODO bug 696166
     let htmlTree = stylePanels[i].cssHtmlTree;
     let cssLogic = htmlTree.cssLogic;
     let elt = eltArray[i];
     let eltId = elt.id;
 
     // Check that the correct node is selected
     is(elt, htmlTree.viewedElement,
       "style inspector node matches the selected node (id=" + eltId + ")");
@@ -144,30 +146,31 @@ function teststylePanels() {
       case "text2":
         is(selector, "#container > span", "correct best match for #text2");
         is(value, "cursive", "correct css property value for #" + eltId);
         break;
       case "container":
         is(selector, "#container", "correct best match for #container");
         is(value, "fantasy", "correct css property value for #" + eltId);
     }
+*/
   }
 
   info("hiding stylePanels[1]");
   Services.obs.addObserver(styleInspectorClosedByHide,
                            "StyleInspector-closed", false);
   stylePanels[1].hidePopup();
 }
 
 function styleInspectorClosedByHide()
 {
   Services.obs.removeObserver(styleInspectorClosedByHide, "StyleInspector-closed", false);
-  ok(stylePanels[0].isOpen(), "instance stylePanels[0] is still open");
-  ok(!stylePanels[1].isOpen(), "instance stylePanels[1] is hidden");
-  ok(stylePanels[2].isOpen(), "instance stylePanels[2] is still open");
+  is(stylePanels[0].state, "open", "instance stylePanels[0] is still open");
+  is(stylePanels[1].state, "closed", "instance stylePanels[1] is hidden");
+  is(stylePanels[2].state, "open", "instance stylePanels[2] is still open");
 
   info("closing web console");
   Services.obs.addObserver(styleInspectorClosedFromConsole1,
                            "StyleInspector-closed", false);
   closeConsole();
 }
 
 function styleInspectorClosedFromConsole1()
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -4573,19 +4573,22 @@ function JSTermHelper(aJSTerm)
       errstr = HUDService.getStr("inspectStyle.nullObjectPassed");
     } else if (!(aNode instanceof Ci.nsIDOMNode)) {
       errstr = HUDService.getStr("inspectStyle.mustBeDomNode");
     } else if (!(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration)) {
       errstr = HUDService.getStr("inspectStyle.nodeHasNoStyleProps");
     }
 
     if (!errstr) {
-      let stylePanel = StyleInspector.createPanel();
-      stylePanel.setAttribute("hudToolId", aJSTerm.hudId);
-      stylePanel.showTool(aNode);
+      let chromeWin = HUDService.getHudReferenceById(aJSTerm.hudId).chromeWindow;
+      let styleInspector = new StyleInspector(chromeWin);
+      styleInspector.createPanel(false, function() {
+        styleInspector.panel.setAttribute("hudToolId", aJSTerm.hudId);
+        styleInspector.open(aNode);
+      });
     } else {
       aJSTerm.writeOutput(errstr + "\n", CATEGORY_OUTPUT, SEVERITY_ERROR);
     }
   };
 
   /**
    * Prints aObject to the output.
    *
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -1949,16 +1949,64 @@ panel[dimmed="true"] {
   outline-offset: -1px;
 }
 
 #highlighter-veil-container[locked] > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
   box-shadow: 0 0 0 1px black;
   outline-color: white;
 }
 
+/* Highlighter toolbar */
+
+#inspector-toolbar {
+  -moz-appearance: none;
+  padding: 4px 3px;
+  border-top: 1px solid hsla(210, 8%, 5%, .65);
+  box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
+  background-image: -moz-linear-gradient(top, hsl(210,11%,36%), hsl(210,11%,18%));
+}
+
+#inspector-inspect-toolbutton,
+#inspector-tools > toolbarbutton {
+  -moz-appearance: none;
+  min-width: 78px;
+  min-height: 22px;
+  color: hsl(210,30%,85%);
+  text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
+  border: 1px solid hsla(210,8%,5%,.45);
+  border-radius: 3px;
+  background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
+  box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
+}
+
+#inspector-inspect-toolbutton:not([checked]):hover:active,
+#inspector-tools > toolbarbutton:not([checked]):hover:active {
+  border-color: hsla(210,8%,5%,.6);
+  background: -moz-linear-gradient(hsla(220,6%,10%,.3), hsla(212,7%,57%,.15) 65%, hsla(212,7%,57%,.3));
+  box-shadow: 0 0 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15);
+}
+
+#inspector-inspect-toolbutton[checked],
+#inspector-tools > toolbarbutton[checked] {
+  color: hsl(208,100%,60%) !important;
+  border-color: hsla(210,8%,5%,.6) !important;
+  background: -moz-linear-gradient(hsla(220,6%,10%,.6), hsla(210,11%,18%,.45) 75%, hsla(210,11%,30%,.4));
+  box-shadow: 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15);
+}
+
+#inspector-inspect-toolbutton[checked]:hover,
+#inspector-tools > toolbarbutton[checked]:hover {
+  background-color: transparent !important;
+}
+
+#inspector-inspect-toolbutton[checked]:hover:active,
+#inspector-tools > toolbarbutton[checked]:hover:active {
+  background-color: hsla(210,8%,5%,.2) !important;
+}
+
 /*
  * need a "bumpy" background image for this!
  */
 #inspector-horizontal-splitter {
   background: none !important;
   -moz-appearance: none;
   cursor: n-resize;
 }
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -2558,37 +2558,40 @@ panel[dimmed="true"] {
   box-shadow: 0 0 0 1px black;
   outline-color: white;
 }
 
 /* Highlighter toolbar */
 
 #inspector-toolbar {
   -moz-appearance: none;
-  height: 32px;
-  padding: 0 3px;
+  padding: 4px 3px;
   border-top: 1px solid hsla(210, 8%, 5%, .65);
   box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
   background-image: -moz-linear-gradient(top, hsl(210,11%,36%), hsl(210,11%,18%));
 }
 
 #inspector-inspect-toolbutton,
 #inspector-tools > toolbarbutton {
   -moz-appearance: none;
-  width: 78px;
-  margin: 3px 5px;
+  min-width: 78px;
+  min-height: 22px;
   color: hsl(210,30%,85%);
   text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
   border: 1px solid hsla(210,8%,5%,.45);
   border-radius: @toolbarbuttonCornerRadius@;
-  background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1));
-  background-clip: padding-box;
+  background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
   box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
 }
 
+#inspector-inspect-toolbutton > .toolbarbutton-text ,
+#inspector-tools > toolbarbutton  > .toolbarbutton-text {
+  margin: 1px 6px;
+}
+
 #inspector-inspect-toolbutton:not([checked]):hover:active,
 #inspector-tools > toolbarbutton:not([checked]):hover:active {
   border-color: hsla(210,8%,5%,.6);
   background: -moz-linear-gradient(hsla(220,6%,10%,.3), hsla(212,7%,57%,.15) 65%, hsla(212,7%,57%,.3));
   box-shadow: 0 0 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15);
 }
 
 #inspector-inspect-toolbutton[checked],
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -2660,16 +2660,64 @@ panel[dimmed="true"] {
   outline-offset: -1px;
 }
 
 #highlighter-veil-container[locked] > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
   box-shadow: 0 0 0 1px black;
   outline-color: white;
 }
 
+/* Highlighter toolbar */
+
+#inspector-toolbar {
+  -moz-appearance: none;
+  padding: 4px 3px;
+  border-top: 1px solid hsla(211,68%,6%,.65) !important;
+  box-shadow: 0 1px 0 hsla(209,29%,72%,.25) inset;
+  background-image: -moz-linear-gradient(top, hsl(209,18%,34%), hsl(210,24%,16%));
+}
+
+#inspector-inspect-toolbutton,
+#inspector-tools > toolbarbutton {
+  -moz-appearance: none;
+  min-width: 78px;
+  min-height: 22px;
+  color: hsl(210,30%,85%);
+  text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
+  border: 1px solid hsla(211,68%,6%,.5);
+  border-radius: 3px;
+  background: -moz-linear-gradient(hsla(209,13%,54%,.35), hsla(209,13%,54%,.1) 85%, hsla(209,13%,54%,.2)) padding-box;
+  box-shadow: 0 1px 0 hsla(209,29%,72%,.15) inset, 0 0 0 1px hsla(209,29%,72%,.1) inset, 0 0 0 1px hsla(209,29%,72%,.1), 0 1px 0 hsla(210,16%,76%,.1);
+}
+
+#inspector-inspect-toolbutton > .toolbarbutton-icon,
+#inspector-tools > toolbarbutton  > .toolbarbutton-icon {
+  margin: 0;
+}
+
+#inspector-inspect-toolbutton:not([checked]):hover:active,
+#inspector-tools > toolbarbutton:not([checked]):hover:active {
+  background-color: hsla(210,18%,9%,.1);
+  background-image: -moz-linear-gradient(hsla(209,13%,54%,.35), hsla(209,13%,54%,.1) 85%, hsla(209,13%,54%,.2));
+  box-shadow: 0 1px 3px hsla(211,68%,6%,.5) inset, 0 0 0 1px hsla(209,29%,72%,.1), 0 1px 0 hsla(210,16%,76%,.1);
+}
+
+#inspector-inspect-toolbutton[checked],
+#inspector-tools > toolbarbutton[checked] {
+  border-color: hsla(211,68%,6%,.6);
+  background: -moz-linear-gradient(hsla(211,68%,6%,.1), hsla(211,68%,6%,.2));
+  box-shadow: 0 1px 3px hsla(211,68%,6%,.5) inset, 0 0 0 1px hsla(209,29%,72%,.1), 0 1px 0 hsla(210,16%,76%,.1);
+  color: hsl(200,100%,60%) !important;
+}
+
+#inspector-inspect-toolbutton[checked]:hover:active,
+#inspector-tools > toolbarbutton[checked]:hover:active {
+  background-color: hsla(211,68%,6%,.2);
+}
+
 /*
  * need a "bumpy" background image for this!
  */
 #inspector-horizontal-splitter {
   background: none !important;
   -moz-appearance: none;
   cursor: n-resize;
 }
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -196,67 +196,76 @@
     <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
                  class="mediaControlsFrame">
         <stack flex="1">
             <vbox flex="1" class="statusOverlay" hidden="true">
                 <box class="statusIcon"/>
             </vbox>
 
             <vbox class="statsOverlay" hidden="true">
-                <html:table class="statsTable" xmlns="http://www.w3.org/1999/xhtml">
-                    <tr>
-                        <td class="statLabel">&stats.media;</td>
-                        <td colspan="3" class="statValue filename"><span class="statFilename"/></td>
-                    </tr>
-                    <tr>
-                        <td class="statLabel">&stats.size;</td>
-                        <td colspan="3" class="statValue size"><span class="statSize"/></td>
-                    </tr>
-                    <tr style="height: 1em;"/>
+                <html:div class="statsDiv" xmlns="http://www.w3.org/1999/xhtml">
+                    <table class="statsTable">
+                        <tr>
+                            <td class="statLabel">&stats.media;</td>
+                            <td class="statValue filename"><span class="statFilename"/></td>
+                        </tr>
+                        <tr>
+                            <td class="statLabel">&stats.size;</td>
+                            <td class="statValue size"><span class="statSize"/></td>
+                        </tr>
+                        <tr style="height: 1em;"/>
 
-                    <tr>
-                        <td class="statLabel">&stats.activity;</td> <td class="statValue activity">
-                          <span class="statActivity">
-                            <span class="statActivityPaused">&stats.activityPaused;</span>
-                            <span class="statActivityPlaying">&stats.activityPlaying;</span>
-                            <span class="statActivityEnded">&stats.activityEnded;</span>
-                            <span class="statActivitySeeking">&stats.activitySeeking;</span>
-                          </span>
-                        </td>
-                        <td class="statLabel">&stats.volume;</td> <td class="statValue"><span class="statVolume"/></td>
-                    </tr>
-                    <tr>
-                        <!-- Localization note: readyState is a HTML5 API MediaElement-specific attribute and should not be localized. -->
-                        <td class="statLabel">readyState</td> <td class="statValue"><span class="statReadyState"/></td>
-                        <td class="statLabel">&stats.channels;</td> <td class="statValue"><span class="statChannels"/></td>
-                    </tr>
-                    <tr>
-                      <!-- Localization note: networkState is a HTML5 API MediaElement-specific attribute and should not be localized. -->
-                      <td class="statLabel">networkState</td> <td class="statValue"><span class="statNetState"/></td>
-                        <td class="statLabel">&stats.sampleRate;</td> <td class="statValue"><span class="statSampleRate"/></td>
-                    </tr>
-                    <tr style="height: 1em;"/>
+                        <tr>
+                            <td class="statLabel">&stats.activity;</td>
+                            <td class="statValue activity">
+                              <span class="statActivity">
+                                <span class="statActivityPaused">&stats.activityPaused;</span>
+                                <span class="statActivityPlaying">&stats.activityPlaying;</span>
+                                <span class="statActivityEnded">&stats.activityEnded;</span>
+                                <span class="statActivitySeeking">&stats.activitySeeking;</span>
+                              </span>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="statLabel">&stats.volume;</td> <td class="statValue"><span class="statVolume"/></td>
+                        </tr>
+                        <tr>
+                            <!-- Localization note: readyState is a HTML5 API MediaElement-specific attribute and should not be localized. -->
+                            <td class="statLabel">readyState</td> <td class="statValue"><span class="statReadyState"/></td>
+                        </tr>
+                        <tr>
+                            <td class="statLabel">&stats.channels;</td> <td class="statValue"><span class="statChannels"/></td>
+                        </tr>
+                        <tr>
+                          <!-- Localization note: networkState is a HTML5 API MediaElement-specific attribute and should not be localized. -->
+                          <td class="statLabel">networkState</td> <td class="statValue"><span class="statNetState"/></td>
+                        </tr>
+                        <tr>
+                          <td class="statLabel">&stats.sampleRate;</td> <td class="statValue"><span class="statSampleRate"/></td>
+                        </tr>
+                        <tr style="height: 1em;"/>
 
-                    <tr>
-                        <td class="statLabel">&stats.framesParsed;</td> <td class="statValue"><span class="statFramesParsed"/></td>
-                        <td class="statLabel"></td> <td class="statValue"></td>
-                    </tr>
-                    <tr>
-                        <td class="statLabel">&stats.framesDecoded;</td> <td class="statValue"><span class="statFramesDecoded"/></td>
-                        <td class="statLabel"></td> <td class="statValue"></td>
-                    </tr>
-                    <tr>
-                        <td class="statLabel">&stats.framesPresented;</td> <td class="statValue"><span class="statFramesPresented"/></td>
-                        <td class="statLabel"></td> <td class="statValue"></td>
-                    </tr>
-                    <tr>
-                        <td class="statLabel">&stats.framesPainted;</td> <td class="statValue"><span class="statFramesPainted"/></td>
-                        <td class="statLabel"></td> <td class="statValue"></td>
-                    </tr>
-                </html:table>
+                        <tr>
+                            <td class="statLabel">&stats.framesParsed;</td>
+                            <td class="statValue"><span class="statFramesParsed"/></td>
+                        </tr>
+                        <tr>
+                            <td class="statLabel">&stats.framesDecoded;</td>
+                            <td class="statValue"><span class="statFramesDecoded"/></td>
+                        </tr>
+                        <tr>
+                            <td class="statLabel">&stats.framesPresented;</td>
+                            <td class="statValue"><span class="statFramesPresented"/></td>
+                        </tr>
+                        <tr>
+                            <td class="statLabel">&stats.framesPainted;</td>
+                            <td class="statValue"><span class="statFramesPainted"/></td>
+                        </tr>
+                    </table>
+                </html:div>
             </vbox>
 
             <vbox class="controlsOverlay">
                 <spacer class="controlsSpacer" flex="1"/>
                 <hbox class="controlBar" hidden="true">
                     <button class="playButton"
                             playlabel="&playButton.playLabel;"
                             pauselabel="&playButton.pauseLabel;"/>
--- a/toolkit/themes/pinstripe/global/media/videocontrols.css
+++ b/toolkit/themes/pinstripe/global/media/videocontrols.css
@@ -177,31 +177,38 @@
   background: url(chrome://global/skin/media/stalled.png) no-repeat center;
 }
 
 .statusIcon[type="error"] {
   background: url(chrome://global/skin/media/error.png) no-repeat center;
 }
 
 /* Statistics formatting */
+html|*.statsDiv {
+  position: relative;
+}
 html|td {
-  padding: 2px;
+  height: 1em;
+  max-height: 1em;
+  padding: 0 2px;
 }
 html|table {
   font-family: Helvetica, Ariel, sans-serif;
   font-size: 11px;
   color: white;
   text-shadow:
     -1px -1px 0 #000,
     1px -1px 0 #000,
     -1px 1px 0 #000,
     1px 1px 0 #000;
-  width: 100%;
+  min-width: 100%;
   background: rgba(68,68,68,.7);
   table-layout: fixed;
+  border-collapse: collapse;
+  position: absolute;
 }
 
 /* CSS Transitions */
 .controlBar:not([immediate]) {
   -moz-transition-property: opacity;
   -moz-transition-duration: 200ms;
 }
 .controlBar[fadeout] {
--- a/toolkit/themes/winstripe/global/media/videocontrols.css
+++ b/toolkit/themes/winstripe/global/media/videocontrols.css
@@ -186,31 +186,38 @@
   background: url(chrome://global/skin/media/stalled.png) no-repeat center;
 }
 
 .statusIcon[type="error"] {
   background: url(chrome://global/skin/media/error.png) no-repeat center;
 }
 
 /* Statistics formatting */
+html|*.statsDiv {
+  position: relative;
+}
 html|td {
-  padding: 2px;
+  height: 1em;
+  max-height: 1em;
+  padding: 0 2px;
 }
 html|table {
   font-family: Helvetica, Ariel, sans-serif;
   font-size: 11px;
   color: white;
   text-shadow:
     -1px -1px 0 #000,
     1px -1px 0 #000,
     -1px 1px 0 #000,
     1px 1px 0 #000;
-  width: 100%;
+  min-width: 100%;
   background: rgba(68,68,68,.7);
   table-layout: fixed;
+  border-collapse: collapse;
+  position: absolute;
 }
 
 /* CSS Transitions */
 .controlBar:not([immediate]) {
   -moz-transition-property: opacity;
   -moz-transition-duration: 200ms;
 }
 .controlBar[fadeout] {