Last Comment Bug 582596 - Style view centered around answering common CSS questions
: Style view centered around answering common CSS questions
Status: RESOLVED FIXED
[strings][styleinspector][minotaur][h...
: uiwanted
Product: Firefox
Classification: Client Software
Component: Developer Tools (show other bugs)
: unspecified
: All All
: P1 normal (vote)
: Firefox 9
Assigned To: Michael Ratcliffe [:miker] [:mratcliffe]
:
Mentors:
Depends on: 572038 579909 683320
Blocks: 583041 586974 586984 587760 588638 589375 590649 591308 591580 591582 591584 591590 591651 592320 592743 593345 652509 652766 654068 656220 663831 672005 672743 672744 672746 672748 672806 674482 674485 674856 680111
  Show dependency treegraph
 
Reported: 2010-07-28 07:49 PDT by Joe Walker [:jwalker] (needinfo me or ping on irc)
Modified: 2011-11-22 02:48 PST (History)
18 users (show)
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---


Attachments
Progress so far on alternative style panel presentation (50.01 KB, patch)
2010-07-28 08:08 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Updated patch (67.65 KB, patch)
2010-08-02 09:00 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Latest version of the patch (48.42 KB, patch)
2010-08-03 08:17 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
How it looks right now (45.53 KB, image/png)
2010-08-03 08:43 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details
New look (45.71 KB, image/png)
2010-08-03 10:22 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details
Latest version of the patch using a XUL Tree (49.38 KB, patch)
2010-08-04 05:13 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Latest XUL Tree version of the patch (50.51 KB, image/png)
2010-08-04 06:38 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details
What it could look like if we used an HTML 'tree' (76.69 KB, image/png)
2010-08-05 01:25 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details
Working HTML tree (104.64 KB, image/png)
2010-08-06 08:09 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details
The CSS logic that is common to both the XUL tree and the HTML tree (20.32 KB, patch)
2010-08-06 08:11 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
HTML tree that sits on top of the CSS Logic patch (30.08 KB, patch)
2010-08-06 08:12 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Test only patch that fully implements jlog and jprofile (10.58 KB, patch)
2010-08-09 10:40 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
The CSS logic that is common to both the XUL tree and the HTML tree (upload 2) (21.18 KB, patch)
2010-08-09 10:40 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
HTML tree that sits on top of the CSS Logic patch (upload 2) (30.72 KB, patch)
2010-08-09 10:41 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
The CSS logic that is common to both the XUL tree and the HTML tree (upload 3) (21.96 KB, patch)
2010-08-10 05:15 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Test only patch that fully implements jlog and jprofile (upload 2) (10.39 KB, patch)
2010-08-10 05:16 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
HTML tree that sits on top of the CSS Logic patch (upload 3) (31.23 KB, patch)
2010-08-10 05:16 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
The CSS logic that is common to both the XUL tree and the HTML tree (upload 4) (21.45 KB, patch)
2010-08-12 07:46 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
HTML tree that sits on top of the CSS Logic patch (upload 4) (37.11 KB, patch)
2010-08-12 07:47 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
CSS logic (upload 5) (21.14 KB, patch)
2010-08-13 06:18 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
HTML tree (depends on CSS logic patch) (upload 5) (43.86 KB, patch)
2010-08-13 06:20 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
CSS logic (upload 6) (48.58 KB, patch)
2010-08-13 11:28 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
HTML tree (depends on CSS logic patch) (upload 6) (48.58 KB, patch)
2010-08-13 11:29 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
CSS logic (upload 7) (21.19 KB, patch)
2010-08-13 11:29 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
CSS logic (upload 8) (21.71 KB, patch)
2010-08-13 11:38 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
HTML tree (depends on CSS logic patch) (upload 7) (53.43 KB, patch)
2010-08-13 15:06 PDT, Patrick Walton (:pcwalton)
no flags Details | Diff | Review
Split 1: CssLogic (24.15 KB, patch)
2010-08-17 10:16 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 2: the CssHtmlTree (69.90 KB, patch)
2010-08-17 10:18 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
screenshot (256.75 KB, image/png)
2010-08-18 13:58 PDT, Mihai Sucan [:msucan]
no flags Details
Split 1b: CssLogic (24.56 KB, patch)
2010-08-19 06:35 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 2b: the CssHtmlTree (67.17 KB, patch)
2010-08-19 06:38 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 2c: The CssHtmlTree (rebased for tree panel integration) (64.49 KB, patch)
2010-08-19 07:42 PDT, Rob Campbell [:rc] (:robcee)
no flags Details | Diff | Review
Split 1c: CssLogic (25.08 KB, patch)
2010-08-19 11:37 PDT, Mihai Sucan [:msucan]
dolske: review-
Details | Diff | Review
Split 2d: the CssHtmlTree (68.37 KB, patch)
2010-08-19 11:40 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 1d: CssLogic (24.39 KB, patch)
2010-08-25 05:34 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 2e: the CssHtmlTree (69.99 KB, patch)
2010-08-25 05:35 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 1e: CssLogic (25.69 KB, patch)
2010-08-25 06:15 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 1f: CssLogic (25.69 KB, patch)
2010-08-25 06:18 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 2f: the CssHtmlTree (66.82 KB, patch)
2010-08-25 06:20 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 2g: the CssHtmlTree (69.88 KB, patch)
2010-08-25 08:00 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 1g: CssLogic (25.00 KB, patch)
2010-08-25 10:19 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 2h: the CssHtmlTree (62.71 KB, patch)
2010-08-25 10:20 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 1h: CssLogic (25.68 KB, patch)
2010-08-25 13:44 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 2i: the CssHtmlTree (73.75 KB, patch)
2010-08-25 13:51 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 1i: CssLogic (24.43 KB, patch)
2010-08-26 10:50 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 2j: the CssHtmlTree (73.41 KB, patch)
2010-08-26 10:51 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 1j: CssLogic (24.42 KB, patch)
2010-08-26 18:03 PDT, Patrick Walton (:pcwalton)
no flags Details | Diff | Review
Split 2k: the CssHtmlTree (73.41 KB, patch)
2010-08-26 18:18 PDT, Patrick Walton (:pcwalton)
no flags Details | Diff | Review
Split 1j: CssLogic (23.86 KB, patch)
2010-08-27 08:56 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 2l: the CssHtmlTree (68.36 KB, patch)
2010-08-27 09:07 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 2m: the CssHtmlTree (74.43 KB, patch)
2010-08-29 07:10 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 2n: the CssHtmlTree (75.35 KB, patch)
2010-08-29 10:25 PDT, Mihai Sucan [:msucan]
l10n: review-
Details | Diff | Review
Split 1k: CssLogic (24.00 KB, patch)
2010-08-30 11:18 PDT, Mihai Sucan [:msucan]
dolske: review+
Details | Diff | Review
Split 2o: the CssHtmlTree (77.32 KB, patch)
2010-08-30 11:23 PDT, Mihai Sucan [:msucan]
dolske: review-
l10n: feedback+
Details | Diff | Review
Split 1l: CssLogic (24.37 KB, patch)
2010-09-03 07:29 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 1m: CssLogic (23.84 KB, patch)
2010-09-03 10:26 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 1n: CssLogic (23.97 KB, patch)
2010-09-04 13:10 PDT, Mihai Sucan [:msucan]
no flags Details | Diff | Review
Split 1: CssLogic. Upload 16 (50.87 KB, patch)
2010-09-20 04:48 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
Split 2: HtmlTree. Upload 16 (101.02 KB, patch)
2010-09-20 04:50 PDT, Joe Walker [:jwalker] (needinfo me or ping on irc)
no flags Details | Diff | Review
CSSLogic + HtmlTree Upload 17 (148.41 KB, patch)
2011-05-02 04:36 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Test only patch that fully implements jlog and jprofile (upload 3) (10.51 KB, patch)
2011-05-02 04:43 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
529448: CSSLogic + HtmlTree Upload 18 (148.08 KB, patch)
2011-05-03 03:59 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Test only patch that fully implements jlog and jprofile (upload 4) (11.66 KB, patch)
2011-05-03 04:01 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Test only patch that fully implements jlog and jprofile (upload 5) (10.51 KB, patch)
2011-05-06 07:28 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
mihai.sucan: review+
Details | Diff | Review
Interdiff of jlog and jprofile diffs (4.19 KB, patch)
2011-05-06 07:29 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
mihai.sucan: review+
Details | Diff | Review
CSSLogic + HtmlTree Upload 19 (158.00 KB, patch)
2011-05-06 07:31 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
mihai.sucan: review-
Details | Diff | Review
Htmltree patch comparison in HTML format (1.52 MB, text/html)
2011-05-06 07:32 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details
New Htmltree patch comparison in HTML format (1.51 MB, text/html)
2011-05-19 05:52 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details
CSSLogic + HtmlTree Upload 20 (173.73 KB, patch)
2011-05-19 06:07 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
mihai.sucan: review-
Details | Diff | Review
New Htmltree patch comparison in HTML format (1.34 MB, text/html)
2011-05-30 03:22 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details
CSSLogic + HtmlTree Upload 21 (142.59 KB, patch)
2011-05-30 03:29 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
CSSLogic + HtmlTree Upload 22 (142.59 KB, patch)
2011-05-31 09:22 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
mihai.sucan: review-
Details | Diff | Review
Style inspector screenshot (37.17 KB, image/png)
2011-06-07 07:03 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details
CSSLogic + HtmlTree Upload 22 (156.72 KB, patch)
2011-06-07 07:59 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
mihai.sucan: review+
Details | Diff | Review
Interdiff (59.58 KB, patch)
2011-06-07 10:04 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Style Inspector complete patch (155.39 KB, patch)
2011-06-10 05:24 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Style Inspector complete patch 2 (155.26 KB, patch)
2011-06-27 01:30 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
dolske: review-
Details | Diff | Review
Style Inspector complete patch 3 (165.34 KB, patch)
2011-07-06 02:04 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Style Inspector complete patch 4 (165.22 KB, patch)
2011-07-20 01:23 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Style Inspector complete patch 5 (165.08 KB, patch)
2011-07-28 12:25 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
dolske: review-
Details | Diff | Review
Style Inspector complete patch 6 (175.19 KB, patch)
2011-08-09 09:34 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Style Inspector complete patch 7 (176.62 KB, patch)
2011-08-14 10:21 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
dolske: review-
Details | Diff | Review
Style Inspector complete patch 8 (181.12 KB, patch)
2011-08-19 04:08 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
dolske: review-
Details | Diff | Review
Style Inspector complete patch 9 (180.63 KB, patch)
2011-08-24 05:07 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
Style Inspector complete patch 10 (180.67 KB, patch)
2011-08-24 06:02 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
dolske: review+
Details | Diff | Review
Style Inspector complete patch 11 (178.86 KB, patch)
2011-08-25 00:49 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review
[checked-in] 555671: Style Inspector complete patch 12 (178.87 KB, patch)
2011-08-29 06:30 PDT, Michael Ratcliffe [:miker] [:mratcliffe]
no flags Details | Diff | Review

Description Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-07-28 07:49:53 PDT
The current style view(s) are a brain dump of information that is hard to navigate, it would be a big benefit to users if the CSS information was presented in an easier to use format.
Comment 1 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-07-28 08:08:28 PDT
Created attachment 460876 [details] [diff] [review]
Progress so far on alternative style panel presentation

Just moving to XUL tree rather than HTML tree
Comment 2 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-07-30 08:11:51 PDT
Comment on attachment 460876 [details] [diff] [review]
Progress so far on alternative style panel presentation

# HG changeset patch
# User Joe Walker <jwalker@mozilla.com>
# Date 1280480973 -3600
# Node ID e42c10b75358f8c489cc09723ba08a77d95030e0
# Parent c5f35032f22df63a99d63bba7cf791ae309d0ab7
Addition of an alternative way to view style information - see bug 582596

diff -r c5f35032f22d browser/base/content/browser-sets.inc
--- a/browser/base/content/browser-sets.inc	Fri Jul 30 11:07:42 2010 +0200
+++ b/browser/base/content/browser-sets.inc	Fri Jul 30 16:07:43 2010 +0100
@@ -152,6 +152,8 @@
              disabled="true"/>
     <command id="Inspector:Style"
              oncommand="InspectorUI.toggleStylePanel();"/>
+    <command id="Inspector:Style2"
+             oncommand="InspectorUI.style2.togglePanel();"/>
   </commandset>
 
   <broadcasterset id="mainBroadcasterSet">
diff -r c5f35032f22d browser/base/content/browser.js
--- a/browser/base/content/browser.js	Fri Jul 30 11:07:42 2010 +0200
+++ b/browser/base/content/browser.js	Fri Jul 30 16:07:43 2010 +0100
@@ -164,6 +164,7 @@
 
 #include browser-fullZoom.js
 #include inspector.js
+#include style2.js
 #include browser-places.js
 #include browser-tabPreviews.js
 
diff -r c5f35032f22d browser/base/content/browser.xul
--- a/browser/base/content/browser.xul	Fri Jul 30 11:07:42 2010 +0200
+++ b/browser/base/content/browser.xul	Fri Jul 30 16:07:43 2010 +0100
@@ -252,6 +252,11 @@
                        accesskey="&inspectStyleButton.accesskey;"
                        class="toolbarbutton-text"
                        command="Inspector:Style"/>
+        <toolbarbutton id="inspector-style2-toolbutton"
+                       label="&inspectStyle2Button.label;"
+                       accesskey="&inspectStyle2Button.accesskey;"
+                       class="toolbarbutton-text"
+                       command="Inspector:Style2"/>
       </toolbar>
       <tree id="inspector-tree" class="plain"
             seltype="single"
@@ -291,6 +296,52 @@
         </hbox>
     </panel>
 
+    <panel id="inspector-style2-panel"
+           class="dimmable"
+           orient="vertical"
+           ignorekeys="true"
+           noautofocus="true"
+           noautohide="true"
+           level="top">
+      <toolbar id="inspector-style2-toolbar"
+               nowindowdrag="true">
+        <label>&inspectStyle2PanelTitle.label;</label>
+      </toolbar>
+      <!--
+      <browser id="inspector-style2-browser"
+               flex="1"
+               src="chrome://browser/content/style2.html"
+               onclick="InspectorUI.style2.control.onClick(event);"
+               disablehistory="true" />
+      -->
+      <tree id="inspector-style2-tree" class="plain"
+            seltype="single"
+            treelines="true"
+            flex="1">
+            <!--
+            onselect="InspectorUI.style2.control.onTreeSelected(event);"
+            -->
+        <treecols>
+          <treecol id="style2Property" label="Property" primary="true"
+                   persist="width,hidden,ordinal" flex="1" width="35%"/>
+          <splitter class="tree-splitter"/>
+          <treecol id="style2Selector" label="Selector"
+                   persist="width,hidden,ordinal" flex="1" width="17%"/>
+          <splitter class="tree-splitter"/>
+          <treecol id="style2Value" label="Value"
+                   persist="width,hidden,ordinal" flex="1" width="30%"/>
+          <splitter class="tree-splitter"/>
+          <treecol id="style2Source" label="Source"
+                   persist="width,hidden,ordinal" flex="1" width="18%"/>
+        </treecols>
+        <treechildren id="inspector-style2-tree-body"/>
+      </tree>
+      <hbox align="end">
+        <spacer flex="1" />
+        <resizer dir="bottomend" />
+      </hbox>
+    </panel>
+
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event);">
       <menuseparator/>
diff -r c5f35032f22d browser/base/content/inspector.js
--- a/browser/base/content/inspector.js	Fri Jul 30 11:07:42 2010 +0200
+++ b/browser/base/content/inspector.js	Fri Jul 30 16:07:43 2010 +0100
@@ -23,6 +23,7 @@
  *
  * Contributor(s):
  *   Rob Campbell <rcampbell@mozilla.com> (original author)
+ *   Joe Walker (jwalker@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
@@ -500,6 +501,114 @@
   selectEventsSuppressed: false,
   inspecting: false,
 
+  style2: {
+    /**
+     * Should the style panel be showing?
+     * TODO: what is the difference between showPanel and panel.state?
+     */
+    showPanel: true,
+
+    /**
+     * The Stylesheet inspection logic. Setup by init()
+     * An instance of CssLogic (see style2.js)
+     */
+    cssLogic: null,
+
+    /**
+     * An instance of CssHtmlTree (see style2.js). Setup by init()
+     * But only if browser.xul contains inspector-style2-browser
+     */
+    control: null,
+
+    /**
+     * An instance of CssXulTreeView (see style2.js). Setup by init()
+     * But only if browser.xul contains inspector-style2-tree
+     */
+    treeView: null,
+
+    /**
+     * Reference to panel#inspector-style2-panel from browser.xul
+     */
+    panel: null,
+
+    /**
+     * TODO: This should probably be a real constructor, but we're all static
+     */
+    init: function IUI_style2_init()
+    {
+      this.panel = document.getElementById("inspector-style2-panel");
+      this.panel.hidden = false;
+
+      this.cssLogic = new CssLogic(InspectorUI.style);
+
+      let styleWin = document.getElementById("inspector-style2-browser");
+      if (styleWin) {
+        this.control = new CssHtmlTree(styleWin, this.cssLogic);
+      }
+
+      // Setup the XUL tree
+      let tree = document.getElementById("inspector-style2-tree");
+      if (tree) {
+        this.treeView = new CssXulTreeView(this.cssLogic);
+        tree.view = this.treeView;
+      }
+
+      if (this.showPanel) {
+        this.openPanel();
+      }
+    },
+
+    /**
+     * Toggle the style2 panel.
+     * Invoked from the toolbar's Style2 button (browser-sets.inc)
+     */
+    togglePanel: function IUI_style2_togglePanel()
+    {
+      if (this.showPanel) {
+        this.panel.hidePopup();
+      } else {
+        this.openPanel();
+        if (InspectorUI.treeView.selectedNode) {
+          this.highlight(InspectorUI.treeView.selectedNode);
+        }
+      }
+      this._showPanel = !this._showPanel;
+    },
+
+    highlight: function IUI_style2_highlight(element)
+    {
+      this.cssLogic.highlight(element);
+
+      if (this.control) {
+        this.control.highlight(element);
+      }
+      if (this.treeView) {
+        this.treeView.highlight(element);
+      }
+    },
+
+    /**
+     * Display this.panel on the screen
+     */
+    openPanel: function IUI_style2_openPanel()
+    {
+      if (!this.isOpen()) {
+        // open at top right of browser panel, offset by 20px from top.
+        this.panel.openPopup(InspectorUI.browser, "end_before", 0, -20, false, false);
+        // size panel to 200px wide by half browser height - 60.
+        this.panel.sizeTo(200, InspectorUI.win.outerHeight / 2 - 60);
+      }
+    },
+
+    /**
+     * Report on the state of this.panel
+     */
+    isOpen: function IUI_style2_isOpen()
+    {
+      return this.panel && this.panel.state === "open";
+    }
+  },
+
   /**
    * Toggle the inspector interface elements on or off.
    *
@@ -646,6 +755,7 @@
       this.clearStylePanel();
       this.openStylePanel();
     }
+    this.style2.init();
     if (this._showDOMPanel) {
       this.openDOMPanel();
     }
@@ -688,6 +798,9 @@
     if (this.isStylePanelOpen) {
       this.stylePanel.hidePopup();
     }
+    if (this.style2.isOpen()) {
+      this.style2.panel.hidePopup();
+    }
     this.inspectCmd.setAttribute("checked", false);
     this.browser = this.win = null; // null out references to browser and window
   },
@@ -701,6 +814,7 @@
     this.attachPageListeners();
     this.inspecting = true;
     this.toggleDimForPanel(this.stylePanel);
+    this.toggleDimForPanel(this.style2.panel);
   },
 
   /**
@@ -714,8 +828,10 @@
     this.detachPageListeners();
     this.inspecting = false;
     this.toggleDimForPanel(this.stylePanel);
+    this.toggleDimForPanel(this.style2.panel);
     if (this.treeView.selection) {
       this.updateStylePanel(this.treeView.selectedNode);
+      this.style2.highlight(this.treeView.selectedNode);
     }
   },
 
@@ -887,6 +1003,7 @@
     this.highlighter.highlightNode(node);
     this.stopInspecting();
     this.updateStylePanel(node);
+    this.style2.highlight(node);
     return true;
   },
 
@@ -929,6 +1046,7 @@
     this.treeView.selectedNode = aNode;
     this.selectEventsSuppressed = false;
     this.updateStylePanel(aNode);
+    this.style2.highlight(aNode);
   },
 
   ///////////////////////////////////////////////////////////////////////////
diff -r c5f35032f22d browser/base/content/style2.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browser/base/content/style2.html	Fri Jul 30 16:07:43 2010 +0100
@@ -0,0 +1,117 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+  <style id="dynamic"></style>
+  <title>Style2</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+  <!-- <link rel="stylesheet" href="chrome://browser/skin/style2.css" type="text/css"/> -->
+  <style>
+    body {
+      font-family: Lucida Grande, sans-serif;
+      font-size: 11px;
+    }
+    table.styles { border: 1px solid black; overflow-y: auto; }
+    table.styles { border-collapse: collapse; }
+    table.styles th { background: #EEE; }
+    table.styles th,
+    table.styles td {
+      text-align: left; padding-left: 2px; vertical-align:top;
+    }
+
+    table.styles .twisty { text-align: center; font-size: 80%; width: 10px; }
+
+    .style-name { text-align: right; }
+    .style-src { font-size: 90%; color: #666; padding-left: 20px; }
+    .style-rule { font-family: monospace; padding-left: 20px; }
+
+    .overridden { /*background: #FEA;*/ }
+    .filename { font-family: monospace; color: #006; }
+    /*.docpos { font-size: 80%; vertical-align: top; }*/
+    .docpos { display: none; }
+    .simple .specificity, .defaultonly .default { display: none; }
+    p.specific { margin-top:0px; padding: 2px 0; background: #ddd; }
+    .greyout { color: #666; }
+  </style>
+</head>
+<body role="application">
+
+<!--
+The templates below are inserted into this table.
+To visually debug the templates without running firefox, remove the display:none
+from the templates below and load into a browser
+-->
+<table id="tableDisplay" class="styles simple">
+</table>
+
+<!--
+Explanatory headers (currently unused)
+TODO: If we have time, we should find a way to have this at the top of the
+table, however it needs to be included before the main template and refreshed
+each time because otherwise gecko gets confused about the number of columns
+due to the row hiding that we're doing.
+-->
+<table id="headTemplate" style="display:none;">
+  <tr>
+    <th>&nbsp;</th>
+    <th colspan="2">Name</th>
+    <th>Value</th>
+    <th>Source</th>
+    <th>Selector</th>
+  </tr>
+</table>
+
+<!--
+The main body of the table
+Down triangle (i.e. expanded) = &#9654;
+Right triangle (i.e. hidden) = &#9660;
+-->
+<table id="tableTemplate" class="styles simple" style="display:none;" foreach="style in ${styles}">
+  <tr>
+    <th class="twisty" toggle="${style.property}">&#9654;</th>
+    <th class="style-name" colspan="2">${Style2.preize(style.property)}</th>
+    <th save="${style.valueEle}">${style.value}</th>
+    <th class="style-src" save="${style.sourceEle}">${style.source}</th>
+    <th class="style-rule" save="${style.selectorEle}">${style.selector}</th>
+  </tr>
+  <tr class="hiddenWith${style.property}" save="${style.matchedEle}">
+    <td>&nbsp;</td>
+    <td class="twisty" toggle="${style.property}Matched">&#9654;</td>
+    <td>
+      Overridden
+      <span save="${style.matchedCountEle}">${style.matched ? '(' + style.matched.length + ')' : ''}</span>
+    </td>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+  </tr>
+  <tr class="hiddenWith${style.property}" save="${style.unmatchedEle}">
+    <td>&nbsp;</td>
+    <td class="twisty" toggle="${style.property}Unmatched">&#9654;</td>
+    <td>
+      Unmatched
+      <span save="${style.unmatchedCountEle}">${style.matched ? '(' + style.unmatched.length + ')' : ''}</span>
+    </td>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+  </tr>
+</table>
+
+<!--
+We insert one of these for each set of matched and unmatched style rules
+-->
+<table id="rowTemplate" style="display:none;">
+  <tr class="hiddenWith${name}${match} hiddenWith${name}" foreach="rule in ${container}">
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+    <td>&nbsp;</td>
+    <td>${rule.value}</td>
+    <td class="style-src">${rule.source}</td>
+    <td class="style-rule">${Style2.preize(rule.selector)}</td>
+  </tr>
+</table>
+
+</body>
+</html>
diff -r c5f35032f22d browser/base/content/style2.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browser/base/content/style2.js	Fri Jul 30 16:07:43 2010 +0100
@@ -0,0 +1,1484 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla Inspector Module.
+ *
+ * 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> (original author)
+ *   Joe Walker (jwalker@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 ***** */
+
+/*
+ * Done:
+ * - remove the HTML table
+ * - split out the HTML table code
+ * - update documentation
+ * - de-dupe multiple selectors on a line
+ * - include a count in Overloaded / Unmatched
+ * - fully dig into a property at first open
+ * - dig line numbers out
+ * - remove Overloaded / Unmatched twisty where count = 0
+ * - Make the property column wider, shrink the others
+ * 
+ * Working:
+ * - Try out [selector, value, source] ordering
+ * - fill in ? for actual style values
+ * 
+ * Outstanding tasks:
+ * - make the source be a link
+ * - provide alternate values for color/font-weight/etc
+ * - create specificity calculator class
+ * 
+ * - Add editability
+ * - Try out getting rid of Matched/Unmatched twisties and replacing with a
+ *   'Why not used' column
+ * - Add a stylesheet filter to top
+ */
+
+/**
+ * Other bits of CSS display logic work simply by digging into the actually
+ * styles of a selected element and its parents. This method fails to answer
+ * many questions about why the users styles did not work, specifically because
+ * it only looks at the things that did. This approach takes into account all
+ * the available styles, and shows why each rule is not applicable.
+ * 
+ * <p>This approach can be considerably more costly while a page's stylesheets
+ * are examined, so this class tries to be smart about the information that it
+ * caches.
+ * 
+ * <p>These functions could probably be merged into stylePanel.jsm
+ */
+let CssLogic = function(stylePanel)
+{
+  // Instance of stylePanel.jsm
+  this.stylePanel = stylePanel;
+
+  // Both setup by highlight()
+  this.viewedElement = null;
+  this.viewedDocument = null;
+
+  // The cache of examined CSS properties
+  this.properties = {};
+};
+
+CssLogic.prototype = {
+  /**
+   * Focus on a new element - remove the style caches
+   */
+  highlight: function CssLogic_highlight(viewedElement)
+  {
+    if (this.viewedElement === viewedElement) {
+      return;
+    }
+
+    this.viewedElement = viewedElement;
+
+    let doc = XPCNativeWrapper.unwrap(viewedElement.ownerDocument);
+    if (doc !== this.viewedDocument) {
+      // New document; clear the cache
+      this.viewedDocument = doc;
+      this.properties = {};
+    } else {
+      // Same document, new element; clear the matched/unmatched data
+      for (let property in this.properties) {
+        delete this.properties[property].matched;
+        delete this.properties[property].unmatched;
+      }
+    }
+
+    let win = this.viewedDocument.defaultView;
+    this.computedStyle = win.getComputedStyle(this.viewedElement, "");
+  },
+
+  /**
+   * Dig through the stylesheets and get a data structure like this:
+   * {
+   *   property: 'color',
+   *   value: 'red',
+   *   source: 'styles.css',
+   *   selector: 'p.specific',
+   *   matched: // contains all rules that can apply to viewedElement
+   *   [
+   *     { value:'blue', source:'other.css', selector:'p' },
+   *     { value:'#F80', source:'other.css', selector:'*' },
+   *     ...
+   *   ],
+   *   unmatched: // rules that don't apply to viewedElement
+   *   [
+   *     { value:'white', source:'other.css', selector:'div' },
+   *     ...
+   *   ],
+   *   allStyles: [ union of matched and unmatched ],
+   * }
+   * 
+   * <p>Ideally we would pull out the 'winning' style from the matched array.
+   * Currently we do not know how to do this
+   * @param property The CSS property to look for
+   * @param deep Do we fill in matched|unmatched|allStyles
+   */
+  getCssInfo: function CssLogic_getRulesForProperty(property, deep)
+  {
+    if (!this.viewedElement) {
+      jlog('Warning this.viewedElement = ', this.viewedElement);
+      return {};
+    }
+
+    let cssInfo = this.properties[property];
+    if (!cssInfo) {
+      cssInfo = {
+        property: property,
+        value: this.computedStyle.getPropertyValue(property),
+        source: '',
+        selector: '',
+      };
+      this.properties[property] = cssInfo;
+    }
+
+    if (deep) {
+      if (!cssInfo.allRules) {
+        cssInfo.allRules = [];
+        this._addPropertyRules(property, cssInfo.allRules);
+      }
+      if (!cssInfo.matched) {
+        this._markMatches(property, cssInfo);
+      }
+      // Go back and try to guess at which rule was used by the browser
+      // TODO: When matched is sorted by specificity we can just pick the 1st
+      let winner = [];
+      let winnerIndex = -1;
+      for (let i = 0; i < cssInfo.matched.length; i++) {
+        let rule = cssInfo.matched[i];
+        if (rule.value === cssInfo.value) {
+          winnerIndex = i;
+          winner.push(rule);
+        }
+      }
+      if (winner.length === 1) {
+        cssInfo.matched.splice(winnerIndex, 1);
+        cssInfo.source = winner[0].source;
+        cssInfo.selector = winner[0].selector;
+        jlog('winner', winner, cssInfo);
+      }
+    }
+
+    return cssInfo;
+  },
+
+  /**
+   * Dig through all the sheets looking for matches for <tt>property</tt> and
+   * adding the results to <tt>rules</tt>
+   * @param property {string} The CSS property to look for
+   * @param rules {rule[]} Array to which we add the matching rules. i.e. the
+   * <tt>allRules</tt> member of a cssInfo
+   */
+  _addPropertyRules: function CssLogic_addPropertyRules(property, rules)
+  {
+    let sheets = this.viewedDocument.styleSheets;
+    for (let s = 0; s < sheets.length; s++) {
+      this._addPropertyRulesFromSheet(sheets[s], property, rules);
+    }
+  },
+
+  /**
+   * Dig through a single sheet, calling self recursively when we find an
+   * import rule.
+   * @see _addPropertyRules()
+   */
+  _addPropertyRulesFromSheet: function CssLogic_addPropertyRulesFromSheet(sheet, property, rules)
+  {
+    let self = this;
+
+    // Work out a shorthand for the source
+    // TODO: include line numbers in here
+    let source = sheet.href;
+    if (source) {
+      source = source.split('/');
+      source = source[source.length - 1];
+    } else {
+      source = '[HTML]';
+    }
+
+    let cssRules = sheet.cssRules;
+    jlog('Looking into \'' + source + '\'. Rule count: ' + cssRules.length +
+            '. For ' + property);
+
+    for (let r = 0; r < cssRules.length; r++) {
+      let cssRule = XPCNativeWrapper.unwrap(cssRules[r]);
+      let line = '';
+      if (this.stylePanel) {
+        line = this.stylePanel.domUtils.getRuleLine(cssRule);
+      }
+
+      // Process @import rules. TODO: Do we need to worry about recursion?
+      if (cssRule.styleSheet) {
+        this._examineStylesFromSheet(cssRule.styleSheet);
+      }
+
+      let styles = cssRule.style;
+      if (styles) {
+        for (let t = 0; t < styles.length; t++) {
+          if (styles[t] == property) {
+            let selectorText = cssRule.selectorText;
+            selectorText.split(',').forEach(function(selector) {
+              selector = selector.trim();
+              rules.push({
+                property: property,
+                // TODO: Firebug wraps this in a rgbToHex() function. Eh?
+                value: styles.getPropertyValue(property),
+                source: source + ':' + line,
+                href: sheet.href, // TODO: if null then document.location?
+                line: line,
+                selector: selector
+              });
+            });
+          }
+        }
+      }
+    }
+  },
+
+  /**
+   * Go through the <tt>allRules</tt> member of <tt>cssInfo</tt> and work out
+   * if each rule applies to <tt>viewedElement</tt>
+   */
+  _markMatches: function CssLogic_markMatches(property, cssInfo)
+  {
+    cssInfo.matched = [];
+    cssInfo.unmatched = [];
+
+    let self = this;
+
+    for (let r = 0; r < cssInfo.allRules.length; r++) {
+      let rule = cssInfo.allRules[r];
+
+      // Do we have a selector match?
+      let matched = false;
+      let selectorMatches = self.viewedDocument.querySelectorAll(rule.selector);
+      for (let m = 0; m < selectorMatches.length; m++) {
+        if (selectorMatches[m] == self.viewedElement) {
+          matched = true;
+        }
+      }
+
+      if (matched) {
+        cssInfo.matched.push(rule);
+      } else {
+        cssInfo.unmatched.push(rule);
+      }
+    }
+
+    // TODO: Sort the matched array by selector specificity
+    // TODO: Sort unmatched array by how close the matches are?
+  },
+};
+
+/**
+ * This list of styles is taken directly from Firebug.
+ * We need to decide how to acknowledge this.
+ * We are also likely to significantly change it so ...
+ */
+CssLogic.styleGroups =
+{
+  text: [
+    "font-family",
+    "font-size",
+    "font-weight",
+    "font-style",
+    "color",
+    "text-transform",
+    "text-decoration",
+    "letter-spacing",
+    "word-spacing",
+    "line-height",
+    "text-align",
+    "vertical-align",
+    "direction",
+    "column-count",
+    "column-gap",
+    "column-width"
+  ],
+
+  background: [
+    "background-color",
+    "background-image",
+    "background-repeat",
+    "background-position",
+    "background-attachment",
+    "opacity"
+  ],
+
+  box: [
+    "width",
+    "height",
+    "top",
+    "right",
+    "bottom",
+    "left",
+    "margin-top",
+    "margin-right",
+    "margin-bottom",
+    "margin-left",
+    "padding-top",
+    "padding-right",
+    "padding-bottom",
+    "padding-left",
+    "border-top-width",
+    "border-right-width",
+    "border-bottom-width",
+    "border-left-width",
+    "border-top-color",
+    "border-right-color",
+    "border-bottom-color",
+    "border-left-color",
+    "border-top-style",
+    "border-right-style",
+    "border-bottom-style",
+    "border-left-style",
+    "-moz-border-top-radius",
+    "-moz-border-right-radius",
+    "-moz-border-bottom-radius",
+    "-moz-border-left-radius",
+    "outline-top-width",
+    "outline-right-width",
+    "outline-bottom-width",
+    "outline-left-width",
+    "outline-top-color",
+    "outline-right-color",
+    "outline-bottom-color",
+    "outline-left-color",
+    "outline-top-style",
+    "outline-right-style",
+    "outline-bottom-style",
+    "outline-left-style"
+  ],
+
+  layout: [
+    "position",
+    "display",
+    "visibility",
+    "z-index",
+    "overflow",
+    "white-space",
+    "clip",
+    "float",
+    "clear",
+    "-moz-box-sizing"
+  ],
+
+  other: [
+    "cursor",
+    "list-style-image",
+    "list-style-position",
+    "list-style-type",
+    "marker-offset",
+    "user-focus",
+    "user-select",
+    "user-modify",
+    "user-input"
+  ]
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A 4 layer deep tree to display CSS properties.
+ * <p>Tree levels:
+ * <pre>
+ * - Group:
+ *   - Property:
+ *     - Category:
+ *       - Rule:
+ * <pre>
+ * <p>For information about XUL Trees:
+ * - https://developer.mozilla.org/en/XUL_Tutorial/Tree_View_Details
+ * - https://developer.mozilla.org/en/XUL_Tutorial/Custom_Tree_Views
+ * - https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsITreeView
+ */
+let CssXulTreeView = function(cssLogic)
+{
+  this.cssLogic = cssLogic;
+  this.visibleData = [];
+  for (group in CssLogic.styleGroups) {
+    this.visibleData.push({
+      name: group,
+      container: true,
+      open: false,
+      level: 0
+    });
+  }
+
+  // See https://developer.mozilla.org/en/nsITreeBoxObject
+  this.treeBox = null;
+  this.selection = null;
+};
+
+/**
+ * The LEVEL consts apply to the .level property of the objects in visibleData
+ */
+CssXulTreeView.LEVEL = {
+  GROUP: 0,    // e.g. 'text' See CssLogic.styleGroups
+  PROPERTY: 1, // e.g. 'vertical-align' A CSS property
+  MATCH: 2,    // Overloaded Match | Unmatched.
+  RULE: 3,     // A rule that may or may not match a selected element
+};
+
+/**
+ * Our tree has 4 columns.
+ * TODO: These are not actually used at the moment, is there a use for this?
+ */
+CssXulTreeView.COL = {
+  NAME: 0,     // Dictated by the level
+  SELECTOR: 1, // The CSS selector declared by the rule
+  VALUE: 2,    // What is the CSS value of the rule
+  SOURCE: 3,   // What file and where did the rule come from?
+};
+
+/**
+ * Effectively the above in reverse
+ */
+CssXulTreeView.PROPERTY_FOR_COL = [ 'name', 'selector', 'value', 'source' ];
+
+CssXulTreeView.prototype = {
+  highlight: function(viewedElement)
+  {
+    this.viewedElement = viewedElement;
+    // TODO: Priority: reset the tree, delete the data and re-run the logic in the ctor
+  },
+
+  get rowCount()
+  {
+    return this.visibleData.length;
+  },
+
+  getCellText: function(idx, column)
+  {
+    let property = CssXulTreeView.PROPERTY_FOR_COL[column.index];
+    return this.visibleData[idx][property] || '';
+  },
+
+  isContainer: function(idx)
+  {
+    // TODO: Shouldn't this be this.visibleData[idx].container ?
+    return this.visibleData[idx].level < CssXulTreeView.LEVEL.RULE;
+  },
+
+  isContainerOpen: function(idx)
+  {
+    return this.visibleData[idx].open;
+  },
+
+  isContainerEmpty: function(idx)
+  {
+    let data = this.visibleData[idx];
+    // Only LEVEL MATCH nodes with empty contents are empty.
+    if (data.level !== CssXulTreeView.LEVEL.MATCH) {
+      return false;
+    }
+    return data.contents.length === 0;
+  },
+
+  getParentIndex: function(idx)
+  {
+    // TODO: Should we cache the parent in the visibility data ???
+    let level = this.visibleData[idx].level;
+    if (level === CssXulTreeView.LEVEL.GROUP) {
+      return -1;
+    }
+    // TODO: I think this version is more correct
+    for (let t = idx - 1; t >= 0 ; t--) {
+      if (this.visibleData[t].level < level) {
+        return t;
+      }
+    }
+    /* MDC published version
+    for (let t = idx - 1; t >= 0 ; t--) {
+      if (this.isContainer(t)) {
+        return t;
+      }
+    }
+    */
+  },
+
+  getLevel: function(idx)
+  {
+    return this.visibleData[idx].level;
+  },
+
+  hasNextSibling: function(idx, after)
+  {
+    let thisLevel = this.getLevel(idx);
+    for (let t = after + 1; t < this.visibleData.length; t++) {
+      let nextLevel = this.getLevel(t);
+      if (nextLevel == thisLevel) {
+        return true;
+      }
+      if (nextLevel < thisLevel) {
+        break;
+      }
+    }
+    return false;
+  },
+
+  toggleOpenState: function(idx)
+  {
+    let item = this.visibleData[idx];
+    if (item.level === CssXulTreeView.LEVEL.RULE) {
+      return;
+    }
+
+    if (item.open) {
+      // The close algorithm is indifferent to the level - walk down the tree
+      // starting at the next row looking for the first row of a lower level,
+      // removing all you find
+      // TODO: This algorithm has a problem in that it collapses an entire tree
+      // when a top level node is collapsed. Some trees remember the state of
+      // the closed nodes so when the top level node is re-opened, they all
+      // re-open to their previous state.
+      // This is probably a bug, but a minor one.
+      item.open = false;
+      let thisLevel = this.getLevel(idx);
+      let deletecount = 0;
+      for (let t = idx + 1; t < this.visibleData.length; t++) {
+        if (this.getLevel(t) > thisLevel) {
+          deletecount++;
+        }
+        else {
+          break;
+        }
+      }
+      if (deletecount) {
+        this.visibleData.splice(idx + 1, deletecount);
+        this.treeBox.rowCountChanged(idx + 1, -deletecount);
+      }
+    }
+    else {
+      item.open = true;
+
+      let level = this.visibleData[idx].level;
+      if (level === CssXulTreeView.LEVEL.GROUP) {
+        // Add rows for each of the properties in this group
+        let group = this.visibleData[idx].name;
+        let properties = CssLogic.styleGroups[group];
+        for (let i = 0; i < properties.length; i++) {
+          let cssInfo = this.cssLogic.getCssInfo(properties[i], false);
+          cssInfo.name = properties[i];
+          cssInfo.container = true;
+          cssInfo.open = false;
+          cssInfo.level = CssXulTreeView.LEVEL.PROPERTY;
+          this.visibleData.splice(idx + i + 1, 0, cssInfo);
+        }
+        this.treeBox.rowCountChanged(idx + 1, properties.length);
+      }
+
+      else if (level === CssXulTreeView.LEVEL.PROPERTY) {
+        let property = this.visibleData[idx].name;
+        // We could avoid the deep dig here, but then we would not get the
+        // source/selector info, and it's likely that the user will be going
+        // into either matched or unmatched, so we'll need to do it anyway
+        // TODO: do this in a setTimeout?
+        let cssInfo = this.cssLogic.getCssInfo(property, true);
+        jlog('invalidateRow', idx, this.visibleData[idx]);
+        this.treeBox.invalidateRow(idx);
+
+        this.visibleData.splice(idx + 1, 0, {
+          name: 'Overloaded Matches (' + cssInfo.matched.length + ')',
+          parentProperty: property,
+          container: true,
+          open: false,
+          level: CssXulTreeView.LEVEL.MATCH,
+          isMatched: true,
+          contents: cssInfo.matched
+        }, {
+          name: 'Unmatched (' + cssInfo.unmatched.length + ')',
+          parentProperty: property,
+          container: true,
+          open: false,
+          level: CssXulTreeView.LEVEL.MATCH,
+          isMatched: false,
+          contents: cssInfo.unmatched
+        });
+        this.treeBox.rowCountChanged(idx + 1, 2);
+      }
+
+      else if (level === CssXulTreeView.LEVEL.MATCH) {
+        let property = this.visibleData[idx].parentProperty;
+        let cssInfo = this.cssLogic.getCssInfo(property, true);
+
+        let isMatched = this.visibleData[idx].isMatched;
+        let rules = isMatched ? cssInfo.matched : cssInfo.unmatched;
+
+        for (let i = 0; i < rules.length; i++) {
+          let rule = rules[i];
+          rule.name = ''; // The name is higher up in the tree
+          rule.container = false;
+          rule.open = false;
+          rule.level = CssXulTreeView.LEVEL.RULE;
+          this.visibleData.splice(idx + i + 1, 0, rule);
+        }
+        this.treeBox.rowCountChanged(idx + 1, rules.length);
+      }
+    }
+    this.treeBox.invalidateRow(idx);
+  },
+
+  setTree: function(treeBox)
+  {
+    this.treeBox = treeBox;
+  },
+
+  isSeparator: function(idx)
+  {
+    return false;
+  },
+
+  isSorted: function()
+  {
+    return false;
+  },
+
+  isEditable: function(idx, column)
+  {
+    return false;
+  },
+
+  getImageSrc: function(idx, column)
+  {
+  },
+
+  getProgressMode : function(idx,column)
+  {
+  },
+
+  getCellValue: function(idx, column)
+  {
+  },
+
+  cycleHeader: function(col, elem)
+  {
+  },
+
+  selectionChanged: function()
+  {
+  },
+
+  cycleCell: function(idx, column)
+  {
+  },
+
+  performAction: function(action)
+  {
+  },
+
+  performActionOnCell: function(action, index, column)
+  {
+  },
+
+  getRowProperties: function(idx, column, prop)
+  {
+  },
+
+  getCellProperties: function(idx, column, prop)
+  {
+  },
+
+  getColumnProperties: function(column, element, prop)
+  {
+  },
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * 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 The main XUL browser document
+ * @constructor
+ */
+let CssHtmlTree = function(styleWin, cssLogic)
+{
+  // 'window' in that it contains a document
+  this.styleWin = styleWin
+  this.cssLogic = cssLogic;
+
+  // The document in which we display the results
+  this.styleDocument = XPCNativeWrapper.unwrap(this.styleWin.contentWindow.document);
+  // The input template that we use to process the display the results
+  this.tableTemplate = this.styleDocument.getElementById('tableTemplate');
+  // The input template that we use to process the display the results
+  this.rowTemplate = this.styleDocument.getElementById('rowTemplate');
+  // The node in which we display the processed template
+  this.tableDisplay = this.styleDocument.getElementById('tableDisplay');
+  // We edit a custom stylesheet to make the tree work
+  this.customSheet = this.styleDocument.getElementById('dynamic').sheet;
+
+  // The element that we're inspecting
+  this.viewedElement = null;
+  // And the document that it comes from
+  this.viewedDocument = null;
+};
+
+CssHtmlTree.prototype = {
+  /**
+   * TODO: Not sure if we need this it is currently referenced from browser.xul
+   */
+  onTreeSelected: function() {},
+
+  /**
+   * Focus the output display on a specific element
+   * @param element The highlighted node to get styles for.
+   */
+  highlight: function CssHtmlTree_highlight(element)
+  {
+    if (element === this.viewedElement) {
+      return;
+    }
+
+    this.viewedElement = element;
+    this.viewedDocument = XPCNativeWrapper.unwrap(element.ownerDocument);
+
+    this._populateTableTemplate();
+  },
+
+  /**
+   * Used by the twisties in the tree in the style display window to toggle
+   * the display of the child rows.
+   * Handle click events in the html tree panel.
+   * Invoked from browser.xul
+   */
+  onClick: function CssHtmlTree_onClick(event)
+  {
+    // Ignore anything that isn't on a twisty
+    if (!event.target.classList.contains("twisty")) {
+      return;
+    }
+
+    let source = event.target;
+    // The toggle attribute tells us what style we should be toggling
+    let name = source.getAttribute("toggle");
+    if (name) {
+      let cssInfo = this.cssLogic.getCssInfo(name, true);
+
+      if (cssInfo.matchedCountEle) {
+        cssInfo.matchedCountEle.innerHTML = '(' + cssInfo.matched.length + ')';
+      }
+      this._populateRowTemplate(name, cssInfo.matchedEle,
+              cssInfo.matched, 'Matched');
+
+      if (cssInfo.unmatchedCountEle) {
+        cssInfo.unmatchedCountEle.innerHTML = '(' + cssInfo.unmatched.length + ')';
+      }
+      this._populateRowTemplate(name, cssInfo.unmatchedEle,
+              cssInfo.unmatched, 'Unmatched');
+    }
+
+    if (source.collapsed !== 'open') {
+      this._setDisplay('hiddenWith' + name, 'table-row');
+      source.collapsed = 'open';
+      source.innerHTML = '&#9660;';
+    } else {
+      this._setDisplay('hiddenWith' + name, 'none');
+      source.collapsed = 'collapsed';
+      source.innerHTML = '&#9654;';
+    }
+  },
+
+  _populateRowTemplate: function CssHtmlTree_populateRowTemplate(name, tr, container, match)
+  {
+      let data = { container: container, match: match, name: name };
+      let newDiv = this._template(this.rowTemplate, data, true);
+
+      jlog('data', data);
+      jlog('newDiv', newDiv);
+
+      while (newDiv.firstChild) {
+        tr.parentNode.insertBefore(newDiv.firstChild, tr.nextSibling);
+      }
+  },
+
+  /**
+   * Process the main template
+   */
+  _populateTableTemplate: function CssHtmlTree_populateTableTemplate()
+  {
+    let styles = [];
+
+    for (let group in CssLogic.styleGroups) {
+      let properties = CssLogic.styleGroups[group];
+      for (let i = 0; i < properties.length; i++) {
+        let cssInfo = this.cssLogic.getCssInfo(properties[i], false);
+        styles.push(cssInfo);
+      }
+    }
+
+    // Sort the styles by name - remove this when we split things into groups
+    styles.sort(function(cssInfo1, cssInfo2) {
+      return cssInfo1.property.localeCompare(cssInfo2.property);
+    });
+
+    let newTable = this._template(this.tableTemplate, { styles: styles }, true);
+
+    this.tableDisplay.innerHTML = '';
+    while (newTable.firstChild) {
+      while (newTable.firstChild.firstChild) {
+        this.tableDisplay.appendChild(newTable.firstChild.firstChild);
+      }
+      newTable.removeChild(newTable.firstChild);
+    }
+  },
+
+  /**
+   * Hide all the rows
+   */
+  _hideHidden: function CssHtmlTree_hideHidden()
+  {
+    let self = this;
+    for (let group in CssLogic.styleGroups) {
+      CssLogic.styleGroups[group].forEach(function(property) {
+        self._setDisplay('hiddenWith' + property, 'none');
+        self._setDisplay('hiddenWith' + property + 'Matched', 'none');
+        self._setDisplay('hiddenWith' + property + 'Unmatched', 'none');
+      });
+    }
+  },
+
+  /**
+   * Set the display of a given <tt>className</tt> to the given
+   * <tt>newDisplay</tt> using stylesheet manipulation.
+   */
+  _setDisplay: function CssHtmlTree_setDisplay(className, newDisplay)
+  {
+    return;
+
+    // Loop over the rules until we find a match, then remove it
+    for (let r = 0; r < this.customSheet.cssRules.length; r++) {
+      let selector = this.customSheet.cssRules[r].selectorText;
+      if (selector === '.' + className || selector === 'tr.' + className) {
+        this.customSheet.deleteRule(r);
+      }
+    }
+  
+    let prefix = newDisplay === 'none' ? 'tr' : '';
+    let ruleText = prefix + '.' + className + ' { display: ' + newDisplay + '; }';
+    this.customSheet.insertRule(ruleText, 0);
+  },
+
+  /**
+   * Clone the given template node, and process it by resolving ${} references
+   * in the template
+   */
+  _template: function CssHtmlTree_template(template, data, returnDummyParent) {
+    data = data || {};
+    let duplicated = template.cloneNode(true);
+
+    // Looping might ask for a parentNode, so we give it a dummy
+    let parent = template.ownerDocument.createElement('div');
+    parent.appendChild(duplicated);
+
+    let templater = new Templater();
+    templater.processNode(duplicated, data);
+    return returnDummyParent ? parent : duplicated;
+  },
+};
+
+/**
+ * 'static' function used by the style2.html template to turn breaking dashes
+ * into non-breaking ones (i.e. unicode decimal 8722).
+ * This function might be filled out to swap breaking spaces into non-breaking
+ * ones too, however for now it's only for style names.
+ */
+CssHtmlTree.preize = function(name)
+{
+  return name.replace(/-/g, '\u2212');
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+function Templater() {
+};
+
+/**
+ * Recursive function to walk the tree processing the attributes as it goes.
+ */
+Templater.prototype.processNode = function(node, data) {
+    var self = this;
+    var recurse = true;
+    // Process attributes
+    if (node.attributes && node.attributes.length) {
+        // It's good to clean up the attributes when we've processed them,
+        // but if we do it straight away, we mess up the array index
+        var attrs = Array.prototype.slice.call(node.attributes);
+        for (var i = 0; i < attrs.length; i++) {
+            var value = attrs[i].value;
+            var name = attrs[i].name;
+
+            if (name === 'save') {
+                // Save attributes are a setter using the node
+                value = self.stripBraces(value);
+                self.property(value, data, node);
+                node.removeAttribute('save');
+            } else if (name === 'if') {
+                value = self.stripBraces(value);
+                try {
+                    var reply = self.envEval(value, data, attrs[i].value);
+                    recurse = !!reply;
+                } catch (ex) {
+                    this.error('Error with \'', value, '\'', ex);
+                    recurse = false;
+                }
+                if (!recurse) {
+                    node.parentNode.removeChild(node);
+                }
+                node.removeAttribute('if');
+            } else if (name === 'foreach') {
+                var paramName = 'param';
+                if (value.charAt(0) === '$') {
+                    // No custom loop variable name. Use the default: 'param'
+                    value = self.stripBraces(value);
+                } else {
+                    // Extract the loop variable name from 'NAME in ${ARRAY}'
+                    var nameArr = value.split(' in ');
+                    paramName = nameArr[0].trim();
+                    value = self.stripBraces(nameArr[1].trim());
+                }
+                recurse = false;
+                try {
+                    var array = self.envEval(value, data, attrs[i].value);
+                    array.forEach(function(param) {
+                        var clone = node.cloneNode(true);
+                        clone.removeAttribute('foreach');
+                        node.parentNode.insertBefore(clone, node);
+                        data[paramName] = param;
+                        self.processNode(clone, data);
+                        delete data[paramName];
+                    });
+                    node.parentNode.removeChild(node);
+                } catch (ex) {
+                    this.error('Error with \'', value, '\'', ex);
+                    recurse = false;
+                }
+                node.removeAttribute('foreach');
+            } else if (name.substring(0, 2) === 'on') {
+                // Event registration relies on property doing a bind
+                value = self.stripBraces(value);
+                var func = self.property(value, data);
+                if (typeof func !== 'function') {
+                    this.error('Expected ' + value +
+                            ' to resolve to a function, but got ', typeof func);
+                }
+                node.removeAttribute(name);
+                var capture = node.hasAttribute('capture' + name.substring(2));
+                node.addEventListener(name.substring(2), func, capture);
+            } else {
+                // Replace references in other attributes
+                var newValue = value.replace(/\$\{[^}]*\}/g, function(path) {
+                    return self.envEval(path.slice(2, -1), data, value);
+                });
+                // Remove '_' prefix of attribute names so the DOM won't try
+                // to use them before we've processed the template
+                if (name.indexOf('_') === 0) {
+                    node.removeAttribute(name);
+                    node.setAttribute(name.substring(1), newValue);
+                } else if (value !== newValue) {
+                    attrs[i].value = newValue;
+                }
+            }
+        }
+    }
+
+    // Process child nodes
+    if (recurse) {
+        self.processChildren(node, data);
+    }
+
+    // Process TextNodes
+    if (node.nodeType === Node.TEXT_NODE) {
+        // Replace references in other attributes
+        value = node.data; // TODO: is this more correct than textContent?
+        // We can't use the string.replace() with function trick because we need
+        // to support functions that return DOM nodes, so we can't have the
+        // conversion to a string.
+        // Instead we process the string as an array of parts. In order to split
+        // the string up, we first replace ${ with \uF001$ and } with \uF002
+        // We can then split using \uF001 or \uF002 to get an array of strings
+        // where scripts are prefixed with $.
+        // \uF001 and \uF002 are just unicode chars reserved for private use.
+        value = value.replace(/\$\{([^}]*)\}/g, '\uF001$$$1\uF002');
+        var parts = value.split(/\uF001|\uF002/);
+        if (parts.length > 1) {
+            parts.forEach(function(part) {
+                if (part === null || part === undefined || part === '') {
+                    return;
+                }
+                if (part.charAt(0) === '$') {
+                    part = self.envEval(part.slice(1), data, node.data);
+                }
+                if (part === null) {
+                    part = "null";
+                }
+                if (part === undefined) {
+                    part = "undefined";
+                }
+                // Hmmm isDOMElement ...
+                if (typeof part.cloneNode !== 'function') {
+                    part = node.ownerDocument.createTextNode(part.toString());
+                }
+                node.parentNode.insertBefore(part, node);
+            });
+            node.parentNode.removeChild(node);
+        }
+    }
+};
+
+/**
+ * Loop through the child nodes of the given node, calling processNode on them
+ * all. Note this first clones the set of nodes, so the set of nodes that we
+ * visit will be unaffected by additions or removals.
+ * @param node The node from which to find children to visit.
+ * @param data The data to pass to processNode
+ */
+Templater.prototype.processChildren = function(node, data) {
+    var children = Array.prototype.slice.call(node.childNodes);
+    for (var i = 0; i < children.length; i++) {
+        this.processNode(children[i], data);
+    }
+};
+
+/**
+ * Warn of string does not begin '${' and end '}'
+ * @return The string stripped of ${ and }, or untouched if it does not match
+ */
+Templater.prototype.stripBraces = function(str) {
+    if (!str.match(/\$\{.*\}/g)) {
+        this.error('Expected ' + str + ' to match ${...}');
+        return str;
+    }
+    return str.slice(2, -1);
+};
+
+/**
+ * Combined getter and setter that works with a path through some data set.
+ * For example:<ul>
+ * <li>property('a.b', { a: { b: 99 }}); // returns 99
+ * <li>property('a', { a: { b: 99 }}); // returns { b: 99 }
+ * <li>property('a', { a: { b: 99 }}, 42); // returns 99 and alters the
+ * input data to be { a: { b: 42 }}
+ * </ul>
+ * @param path An array of strings indicating the path through the data, or
+ * a string to be cut into an array using <tt>split('.')</tt>
+ * @param data An object to look in for the <tt>path</tt>
+ * @param newValue (optional) If undefined, this value will replace the
+ * original value for the data at the path specified.
+ * @returns The value pointed to by <tt>path</tt> before any
+ * <tt>newValue</tt> is applied.
+ */
+Templater.prototype.property = function(path, data, newValue) {
+    if (typeof path === 'string') {
+        path = path.split('.');
+    }
+    var value = data[path[0]];
+    if (path.length === 1) {
+        if (newValue !== undefined) {
+            data[path[0]] = newValue;
+        }
+        if (typeof value === 'function') {
+            return value.bind(data);
+        }
+        return value;
+    }
+    if (!value) {
+        this.error('Can\'t find path=', path, " in data=", data);
+        return null;
+    }
+    return this.property(path.slice(1), value, newValue);
+};
+
+/**
+ * Like eval, but that creates a context of the variables in <tt>env</tt> in
+ * which the script is evaluated.
+ * WARNING: This script uses 'with' which is generally regarded to be evil.
+ * The alternative is to create a Function at runtime that takes X parameters
+ * according to the X keys in the env object, and then call that function using
+ * the values in the env object. This is likely to be slow, but workable.
+ * @param script The string to be evaluated
+ * @param env The environment in which to eval the script.
+ * @param context Optional debugging string in case of failure
+ * @returns The return value of the script
+ */
+Templater.prototype.envEval = function(script, env, context) {
+    with (env) {
+        try {
+            return eval(script);
+        }
+        catch (ex) {
+            var message = 'Error evaluating \'' + script + '\'';
+            if (context) {
+                message += ' within \'' + context + '\'';
+            }
+            this.error(message + ': ' + ex);
+            return message;
+        }
+    }
+};
+
+/**
+ * A generic way of reporting errors, for easy overloading in different
+ * environments.
+ */
+Templater.prototype.error = function() {
+    jlog(arguments);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+var lastTime = new Date().getTime();
+var lastName = null;
+function jprofile(name) {
+    var now = new Date().getTime();
+    var elapsed = now - lastTime;
+    lastTime = now;
+    if (lastName) {
+        if (name) {
+            Services.console.logStringMessage('Last job: ' + lastName + ' took ' + (elapsed / 1000) + ' sec. Starting ' + name);
+        } else {
+            Services.console.logStringMessage('Last job: ' + lastName + ' took ' + (elapsed / 1000) + ' sec.');
+        }
+    } else {
+        Services.console.logStringMessage('Starting ' + name);
+    }
+    lastName = name;
+}
+
+function jlog() {
+    var output = '';
+    for (var i = 0; i < arguments.length; i++) {
+        if (i !== 0) {
+            output += '\n';
+        }
+        output += toDescriptiveString(arguments[i], 4);
+    }
+    Services.console.logStringMessage(output);
+}
+
+/**
+ * This function pretty-prints simple data or whole object graphs, for example
+ * as an aid in debugging.
+ * @see http://getahead.org/dwr/browser/util/todescriptivestring
+ */
+var toDescriptiveString = function(data, showLevels, options) {
+  if (showLevels === undefined) {
+    showLevels = 1;
+  }
+  var opt = {};
+  if (options && typeof options == "object") {
+    opt = options;
+  }
+  var defaultoptions = {
+    escapeHtml:false,
+    baseIndent: "",
+    childIndent: "\u00A0\u00A0",
+    lineTerminator: "\n",
+    oneLineMaxItems: 20,
+    shortStringMaxLength: 13,
+    longStringMaxLength: 53,
+    propertyNameMaxLength: 30
+  };
+  for (var p in defaultoptions) {
+    if (!(p in opt)) {
+      opt[p] = defaultoptions[p];
+    }
+  }
+
+  var skipDomProperties = {
+    document:true, ownerDocument:true,
+    all:true,
+    parentElement:true, parentNode:true, offsetParent:true,
+    children:true, firstChild:true, lastChild:true,
+    previousSibling:true, nextSibling:true,
+    innerHTML:true, outerHTML:true,
+    innerText:true, outerText:true, textContent:true,
+    attributes:true,
+    style:true, currentStyle:true, runtimeStyle:true,
+    parentTextEdit:true
+  };
+
+  var shown = [];
+  function isShown(obj) {
+    if (typeof obj !== 'object') {
+      return false;
+    }
+    for (var i = 0; i < shown.length; i++) {
+      if (shown[i] === obj) {
+        return true;
+      }
+    }
+    shown.push(obj);
+    return false;
+  }
+
+  function recursive(data, showLevels, indentDepth, options) {
+    var reply = "";
+    try {
+      // string
+      if (typeof data == "string") {
+        var str = data;
+        if (showLevels == 0 && str.length > options.shortStringMaxLength) {
+            str = str.substring(0, options.shortStringMaxLength - 3) + "...";
+        }
+        else if (str.length > options.longStringMaxLength) {
+            str = str.substring(0, options.longStringMaxLength - 3) + "...";
+        }
+        if (options.escapeHtml) {
+          // Do the escape separately for every line as escapeHtml() on some
+          // browsers (IE) will strip line breaks and we want to preserve them
+          var lines = str.split("\n");
+          for (var i = 0; i < lines.length; i++) {
+            lines[i] = lines[i].
+              replace(/&/g,'&amp;').
+              replace(/</g,'&lt;').
+              replace(/>/g,'&gt;');
+          }
+          str = lines.join("\n");
+        }
+        if (showLevels == 0) { // Short format
+          str = str.replace(/\n|\r|\t/g, function(ch) {
+            switch (ch) {
+              case "\n": return "\\n";
+              case "\r": return "";
+              case "\t": return "\\t";
+              default: throw new Error('logic error');
+            }
+          });
+        }
+        else { // Long format
+          str = str.replace(/\n|\r|\t/g, function(ch) {
+            switch (ch) {
+              case "\n":
+                return options.lineTerminator + indent(indentDepth + 1, options);
+              case "\r":
+                return "";
+              case "\t":
+                return "\\t";
+            }
+          });
+        }
+        reply = '"' + str + '"';
+      }
+
+      // function
+      else if (typeof data == "function") {
+        reply = "function";
+      }
+
+      // Array
+      else if (data && data.join) {
+        if (showLevels == 0) {
+          // Short format (don't show items)
+          reply = (data.length > 0) ? "[...]" : "[]";
+        }
+        else {
+          // Long format (show items)
+          var strarr = [];
+          strarr.push("[");
+          var count = 0;
+          for (var i = 0; i < data.length; i++) {
+            if (! (i in data)) {
+              continue;
+            }
+            var itemvalue = data[i];
+            if (count > 0) {
+              strarr.push(", ");
+            }
+            if (isShown(itemvalue)) {
+              strarr.push(",^^^");
+              continue;
+            }
+            if (showLevels === 1) { // One-line format
+              if (count == options.oneLineMaxItems) {
+                strarr.push("...");
+                break;
+              }
+            }
+            else if (data.length === 1) {
+              strarr.push(" ");
+            }
+            else { // Multi-line format
+              strarr.push(options.lineTerminator + indent(indentDepth+1, options));
+            }
+            if (i != count) {
+              strarr.push(i);
+              strarr.push(":");
+            }
+            strarr.push(recursive(itemvalue, showLevels-1, indentDepth+1, options));
+            count++;
+          }
+          if (data.length === 1) {
+            strarr.push(" ");
+          }
+          else if (showLevels > 1) {
+            strarr.push(options.lineTerminator + indent(indentDepth, options));
+          }
+          strarr.push("]");
+          reply = strarr.join("");
+        }
+      }
+
+      // Objects except Date
+      else if ((data && typeof data == "object") && !((data && data.toUTCString) ? true : false)) {
+        if (showLevels == 0) { // Short format (don't show properties)
+          reply = _detailedTypeOf(data);
+        }
+        else { // Long format (show properties)
+          var strarr = [];
+          if (_detailedTypeOf(data) != "Object") {
+            strarr.push(_detailedTypeOf(data));
+            if (data.valueOf && typeof data.valueOf() != "object") {
+              strarr.push(":");
+              strarr.push(recursive(data.valueOf(), 1, indentDepth, options));
+            }
+          }
+          strarr.push("{");
+          var isDomObject = (data !== null && typeof data === "object" && data.nodeName !== null);
+          var count = 0;
+          var props = [];
+          for (var prop in data) {
+              props.push(prop);
+          }
+          props.sort();
+          var skipped = false;
+          for (var i = 0; i < props.length; i++) {
+            var prop = props[i];
+            try {
+              var propvalue = data[prop];
+              if (isDomObject) {
+                if (typeof propvalue == "function") {
+                  skipped = true;
+                  continue;
+                }
+                if (skipDomProperties[prop]) {
+                  skipped = true;
+                  continue;
+                }
+                if (prop.toUpperCase() == prop) {
+                  skipped = true;
+                  continue;
+                }
+              }
+
+              if (count === 0) {
+                strarr.push(" ");
+              }
+              if (count > 0) {
+                strarr.push(", ");
+              }
+
+              if (showLevels === 1 || props.length === 1) {
+                // One-line format
+                if (count == options.oneLineMaxItems) {
+                  strarr.push("...");
+                  break;
+                }
+              }
+              else {
+                // Multi-line format
+                strarr.push(options.lineTerminator + indent(indentDepth+1, options));
+              }
+
+              strarr.push(prop.length > options.propertyNameMaxLength ? prop.substring(0, options.propertyNameMaxLength-3) + "..." : prop);
+              strarr.push(":");
+              if (isShown(propvalue)) {
+                strarr.push(",^^^");
+                continue;
+              }
+              strarr.push(recursive(propvalue, showLevels-1, indentDepth+1, options));
+              count++;
+            }
+            catch (ex) {
+              if (count > 0) {
+                strarr.push(", ");
+              }
+              strarr.push(prop + ":err");
+            }
+          }
+          if (skipped) {
+            strarr.push(",...");
+          }
+          if (props.length === 1) {
+            strarr.push(" ");
+          }
+          else if (count > 0) {
+            if (showLevels > 1) {
+              strarr.push(options.lineTerminator + indent(indentDepth, options));
+            }
+            else {
+              strarr.push(" ");
+            }
+          }
+          strarr.push("}");
+          reply = strarr.join("");
+        }
+      }
+
+      // undefined, null, number, boolean, Date
+      else {
+        reply = "" + data;
+      }
+
+      return reply;
+    }
+    catch(err) {
+      return (err.message ? err.message : "" + err);
+    }
+  }
+
+  function indent(count, options) {
+    var strarr = [];
+    strarr.push(options.baseIndent);
+    for (var i=0; i<count; i++) {
+      strarr.push(options.childIndent);
+    }
+    return strarr.join("");
+  };
+
+  return recursive(data, showLevels, 0, opt);
+};
+
+var _detailedTypeOf = function(x) {
+  var reply = typeof x;
+  if (reply == "object") {
+    reply = Object.prototype.toString.apply(x);  // Returns "[object class]"
+    reply = reply.substring(8, reply.length-1);  // Just get the class bit
+  }
+  return reply;
+};
diff -r c5f35032f22d browser/base/jar.mn
--- a/browser/base/jar.mn	Fri Jul 30 11:07:42 2010 +0200
+++ b/browser/base/jar.mn	Fri Jul 30 16:07:43 2010 +0100
@@ -27,6 +27,7 @@
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 *       content/browser/credits.xhtml                 (content/credits.xhtml)
 *       content/browser/fullscreen-video.xhtml        (content/fullscreen-video.xhtml)
+*       content/browser/style2.html                   (content/style2.html)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
 *       content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
 *       content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
diff -r c5f35032f22d browser/locales/en-US/chrome/browser/browser.dtd
--- a/browser/locales/en-US/chrome/browser/browser.dtd	Fri Jul 30 11:07:42 2010 +0200
+++ b/browser/locales/en-US/chrome/browser/browser.dtd	Fri Jul 30 16:07:43 2010 +0100
@@ -184,6 +184,10 @@
 <!ENTITY inspectStyleButton.label     "Style">
 <!ENTITY inspectStyleButton.accesskey "S">
 <!ENTITY inspectStylePanelTitle.label  "Style">
+<!ENTITY inspectStyle2Button.label     "Style2">
+<!ENTITY inspectStyle2Button.accesskey "S">
+<!ENTITY inspectStyle2Button.commandkey ";">
+<!ENTITY inspectStyle2PanelTitle.label  "Style2">
 
 <!ENTITY fileMenu.label         "File"> 
 <!ENTITY fileMenu.accesskey       "F">
Comment 3 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-02 09:00:12 PDT
Created attachment 462085 [details] [diff] [review]
Updated patch
Comment 4 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-03 08:17:17 PDT
Created attachment 462403 [details] [diff] [review]
Latest version of the patch
Comment 5 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-03 08:43:58 PDT
Created attachment 462411 [details]
How it looks right now
Comment 6 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-03 10:22:58 PDT
Created attachment 462426 [details]
New look
Comment 7 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-04 05:13:45 PDT
Created attachment 462751 [details] [diff] [review]
Latest version of the patch using a XUL Tree
Comment 8 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-04 06:38:15 PDT
Created attachment 462759 [details]
Latest XUL Tree version of the patch

The sorting is not currently correct. Needs fixing
Comment 9 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-05 01:25:31 PDT
Created attachment 463090 [details]
What it could look like if we used an HTML 'tree'
Comment 10 Rob Campbell [:rc] (:robcee) 2010-08-06 07:37:46 PDT
while this looks cool, I'm a little concerned about the time to reimplement. We have a XUL tree implementation which is coming along and nearly functional, and would allow us to easily edit individual properties.

Do you think you can have the HTML rewrite complete in a week?
Comment 11 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-06 08:09:24 PDT
Created attachment 463545 [details]
Working HTML tree
Comment 12 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-06 08:11:45 PDT
Created attachment 463547 [details] [diff] [review]
The CSS logic that is common to both the XUL tree and the HTML tree
Comment 13 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-06 08:12:39 PDT
Created attachment 463548 [details] [diff] [review]
HTML tree that sits on top of the CSS Logic patch
Comment 14 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-09 10:40:00 PDT
Created attachment 464090 [details] [diff] [review]
Test only patch that fully implements jlog and jprofile
Comment 15 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-09 10:40:44 PDT
Created attachment 464091 [details] [diff] [review]
The CSS logic that is common to both the XUL tree and the HTML tree (upload 2)
Comment 16 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-09 10:41:17 PDT
Created attachment 464092 [details] [diff] [review]
HTML tree that sits on top of the CSS Logic patch (upload 2)
Comment 17 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-10 05:15:45 PDT
Created attachment 464379 [details] [diff] [review]
The CSS logic that is common to both the XUL tree and the HTML tree (upload 3)
Comment 18 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-10 05:16:15 PDT
Created attachment 464380 [details] [diff] [review]
Test only patch that fully implements jlog and jprofile (upload 2)
Comment 19 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-10 05:16:46 PDT
Created attachment 464381 [details] [diff] [review]
HTML tree that sits on top of the CSS Logic patch (upload 3)
Comment 20 Kevin Dangoor 2010-08-12 05:47:43 PDT
I'm requesting blocking status for this bug. I view this as a key feature that is really useful in providing good information to users and will differentiate the new Firefox Inspector from the similar features out there.
Comment 21 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-12 07:46:30 PDT
Created attachment 465239 [details] [diff] [review]
The CSS logic that is common to both the XUL tree and the HTML tree (upload 4)
Comment 22 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-12 07:47:11 PDT
Created attachment 465240 [details] [diff] [review]
HTML tree that sits on top of the CSS Logic patch (upload 4)
Comment 23 Mihai Sucan [:msucan] 2010-08-12 10:47:47 PDT
I looked through the code and here's my feedback and suggestions:

- Every listed property shows the number of rules on the right side. When I 
highlight an element I think it would be nicer to update that number and show 
the number of matched rules. So, when I go for digging deeper into the style 
panel view, I know into which properties to dig sooner rather than later.

The current view makes all properties equal at a glance, irrespective of the 
highlighted element. I'd like to be able to glance over the properties like now 
and see "5/10 rules" (5 matched out of 10 rules), or "5 matched rules". The view does not need to change otherwise. Just this tiny detail of the "N rules".

I agree that the developer needs to see why a property did *not* match. Show "0 
matched rules", and when he/she expands the property, the list of unmatched 
rules will show. No problem. Just the title change is what I am suggesting. ;)

(My suggestion comes as a user of such tools! I'd really like this tiny change.)

- Some web site layouts have 100+ rules for a single common property like color.  
Don't show all of the unmatched rules. Show only the first 10-20 rules, and add 
the option to "show all unmatched rules".

- I'd like to see the computed style value for each property as well, somehow in the GUI. Perhaps add it to the table, as "Computed style → value".

- There should be an option to show shorthand properties for margin-*, 
padding-*, and for other properties, when possible. It makes glancing over 
the styles much quicker.

- In inspector.js, the new InspectorUI.htmlTree property name is confusing. To a newcomer this might seem to refer to an HTML representation of the web page 
DOM tree. I would suggest renaming the property to CssHtmlTree.

- In csslogic.js the this.viewedDocument object is unwrapped, and you access 
properties that are unsafe.  Isn't this a security issue?

Example in CssLogin.matchesQuery():

  let doc = XPCNativeWrapper.unwrap(element.ownerDocument);
  let matches = doc.querySelectorAll(query);

I believe that's not safe. A malicious developer can overwrite that document 
method.

- Nit-picking: style3.html should be renamed. It has that feeling of 
"unnamed01.html". :)

- Similarly, the <title>Style3</title> should be changed to something more 
sensible.

- The <style> tags should have type=text/css for correctness.

- Links to javascript:null break the style panel when clicked. The style panel 
shows a blank page with a "null" string inside. Should be changed to something 
else, I presume?

- In browser.xul, #inspector-style-browser ... I don't think you need the 
onclick, just handle the clicks for the elements you want inside the HTML page. 
Also, the CssHtmlTree.onClick event handler is empty.

- The CssLogic.highlight() and CssHtmlTree.highlight() methods both check for 
element == this.viewedElement. While that is fine, to avoid checking twice for 
the same element, you should add an option to force the refresh of the CssLogic 
and CssHtmlTree object states. This is needed by DOM mutations event handler 
which may call these methods to refresh the panel (and other panels).

- There's an error in the CssHtmlTree.highlight() method:

  // ...
  this.viewedElement = element;
  this.viewedDocument = XPCNativeWrapper.unwrap(element.ownerDocument);
  if (this.viewedElement) {
    this._populateMainTemplates();
  } // ...

You assume that element exists when you set viewedDocument, but then you're not 
sure - you call _populateMainTemplates() only if the viewedElement evaluates to 
true. I think you should check that element does not evaluate to false at the 
beginning of the method and return void before any action is taken.

- In style3.html and style3.js, the #templateProperties template:

  <div class="temp-loop" foreach="property in ${group.properties}">

  let data = { group: this };
  this.tree._template(this.tree.templateProperties, this.element, data);

Why not use data = { properties: this.properties} directly? It doesn't use other stuff from the StyleGroupView.

- In style3.js the PropertyView object has a getter with an argument:

  get value(property) {
    return this.tree.cssLogic.getCssInfo(property).value;
  }

I think you mean:

  get value() {
    return this.tree.cssLogic.getCssInfo(this.name).value;
  }

- If I am not mistaken, the above getter is never used. The #templateRules takes the property values from each rule of CssInfo. The computedStyle value 
that is returned by the above getter is never displayed.

- The click to expand properties fails to work after some time - when I change the selected element, while not inspecting. In the end, the style panel becomes unusable, because I cannot see any rules for any property. Is this a known bug?

- The default style panel width should be larger: style groups have their titles wrapped.

- The code needs internationalization work, for example see the style3.html 
hard-coded strings and style3.js.


Overall, I really like the code. It's really clean and easy to understand. Great work!
Comment 24 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-13 02:47:23 PDT
Great feedback, thanks. I've fixed many of them already, I've got some bugs to raise, and a few to think about.

- "5/10 rules": Is a very nice idea, but it's tricky. To know that there are 5 rules that apply we only need to dig through the stylesheets, we don't need to investigate the element itself, which can take time. I need to spend some more time checking the performance implications of this, but I agree that we should find a way to do it.

- 100+ rules for a single property: Agreed, and I think it's important.

- see the computed style value for each property as well: It's only missing due to the property.value bug.

- shorthand properties for margin: Agreed, we've got work in general to do here. Investigation needed

- s/InspectorUI.htmlTree/InspectorUI.cssHtmlTree/g -> done

- Check use of unwrapping -> On my list

- <title>Style3</title> is now <title>Style</title>

- <style> tags should have type=text/css -> done

- I saw the Links to javascript:null as part of 585566, but I probably should have made it clearer. I have now (see bug comment).

- if (this.viewedElement) check in CssHtmlTree.highlight -> fixed

- Using group.properties in template rather than group -> I'm 51/49 on this, arguing both ways, but I've come down on your side, and made the change.

- PropertyView.value broken -> good spot, fixed, and it was used - see the computedStyle point above

- click to expand properties fails -> Added to my list to investigate

- style panel width should be larger -> Yeah - we need to get this sorted. On my list

- i18n -> Funny I thought there was a separate bug for this, but I was confused with 585567. Will raise one.

- CssHtmlTree.onClick -> removed

- style3.html should be renamed -> Will do

- Double check for element == this.viewedElement -> Added to my list to look at. Thanks.
Comment 25 Julian Viereck 2010-08-13 02:51:13 PDT
(In reply to comment #24)
>
> - 100+ rules for a single property: Agreed, and I think it's important.

I use a max-height to limit the height if there is a lot of output. See this screenshot: https://bug573103.bugzilla.mozilla.org/attachment.cgi?id=464927. This allows the developer to see all of the properties if he wants to but it doesn't blow up the entire panel because there is a lot of output.
Comment 26 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-13 06:18:50 PDT
Created attachment 465634 [details] [diff] [review]
CSS logic (upload 5)

Includes msucans fixes for 585572
Comment 27 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-13 06:20:16 PDT
Created attachment 465635 [details] [diff] [review]
HTML tree (depends on CSS logic patch) (upload 5)

Fixes for many of the comments raised by msucan
Comment 28 Kevin Dangoor 2010-08-13 07:02:57 PDT
the beta 4 train is leaving without this one, but we'll want to be sure to get it reviewed and landed early in beta 5.
Comment 29 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-13 11:28:07 PDT
Created attachment 465751 [details] [diff] [review]
CSS logic (upload 6)
Comment 30 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-13 11:29:06 PDT
Created attachment 465753 [details] [diff] [review]
HTML tree (depends on CSS logic patch) (upload 6)
Comment 31 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-13 11:29:35 PDT
Created attachment 465754 [details] [diff] [review]
CSS logic (upload 7)
Comment 32 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-13 11:38:11 PDT
Created attachment 465756 [details] [diff] [review]
CSS logic (upload 8)
Comment 33 Axel Hecht [:Pike] 2010-08-13 14:39:18 PDT
I'd prefer browser/base/content/csshtmltree.html to be xhtml and to be localized via a DTD as much as possible.

Also, some parts of the CSS in there make me wonder about RTL, Ehsan, can you have a look?
Comment 34 Patrick Walton (:pcwalton) 2010-08-13 15:06:12 PDT
Created attachment 465842 [details] [diff] [review]
HTML tree (depends on CSS logic patch) (upload 7)

HTML tree patch rebased to trunk.
Comment 35 :Ehsan Akhgari (busy, don't ask for review please) 2010-08-13 15:37:56 PDT
(In reply to comment #33)
> Also, some parts of the CSS in there make me wonder about RTL, Ehsan, can you
> have a look?

This is bad.  Things like text-align: left should be changed to text-align: start, margin-left to -moz-margin-start, padding-right to -moz-padding-end (I might have missed some stuff), and float properties need to be overriden in RTL mode (possibly by a chromedir like trick).
Comment 36 Mihai Sucan [:msucan] 2010-08-14 03:35:09 PDT
Ehsan and Axel: thank you for your suggestions for improving the internationalization of the Style panel.

I'd like to make the changes you have requested, but I still have some questions:

1. Do I reuse an existing DTD or do I create a new one?  If I have to create a new one, should I put it in the same location as browser.dtd? If not, to which DTD should I add the new strings?

2. As you can see in csshtmltree.html, the HTML templates have almost no actual strings, they mostly have placeholders like ${variable}. Those are generated dynamically by the csshtmltree.js script. Some of those variables are strings that need localization. Do I use inspector.properties for those, or do I ensure from the JS that I pass entity names that are added into the HTML?

Thank you!
Comment 37 Mihai Sucan [:msucan] 2010-08-17 10:16:05 PDT
Created attachment 466686 [details] [diff] [review]
Split 1: CssLogic

Adding the updated patches, ready for review.

Details regarding internationalization:

- I have added more strings that I initially left out by mistake (in CssLogic).
- Axel: As agreed with Robert Campbell we are sticking to string bundles 
(inspector.properties) due to the situation we have. The style panel is not a 
typical XUL/XHTML panel that has strings. The entire UI is built from 
JavaScript where we can best use string bundles.

Once again, thank you for your feedback for l10n matters! If there are any other changes we have to make, please let us know!


Patch changes:

- Removed debug code.
- Renamed csshtmltree.html to csshtmltree.xhtml as requested by Axel Hecht.
- Comments adjusted to include type information for arguments and returns.
- Renamed arguments to use the aArgumentName coding style.
- Gave names to anonymous functions/methods, as per coding style guide lines.
- Switched from single quotes to double quotes, as per coding style guide lines.
- Cleaned-up TODOs and added bug numbers for remaining TODOs.
- Fix indentation differences in CssHtmlTree.js.
- Fixed a bug in the Templater: it did not remove the captureEventName
attribute after using it, from the node that is processed. See 
Templater.processNode().
- Made the changes Ehsan recommended for better RTL support. This needs testing.
- Fixed an infinite loop in CssLogic.getShortName().
- Renamed CssSheet.source to CssSheet.shortSource, as was requested in a TODO.
- Made the changes suggested in bug 586980 - added missing properties.
- Integrated the patch from bug 587147 which fixes group view toggling.
- Fixed bug 585575 - the style panel takes into account properties attached to
the element itself (element.style).
- Fixed a bug which caused PropertyView.ruleTitle to execute after the 
Inspector style panel was closed, which in turn ended up in throwing an 
exception.
- Fixed a bug which causes the Inspector code to throw when there is no 
selected node.
- Made a fix for browser_inspector_initilization.js test file which failed 
sometimes.
- Changed browser_inspector_stylePanel.js to work with the new Style panel.

(this list includes the changes I did in the CssHtmlTree patch that I'll attach in a few moments)
Comment 38 Mihai Sucan [:msucan] 2010-08-17 10:18:08 PDT
Created attachment 466687 [details] [diff] [review]
Split 2: the CssHtmlTree

The CssHtmlTree patch. This needs the CssLogic patch.
Comment 39 Mihai Sucan [:msucan] 2010-08-18 13:58:13 PDT
Created attachment 467149 [details]
screenshot

This is a screenshot of the latest Style panel patches.
Comment 40 Mihai Sucan [:msucan] 2010-08-19 06:35:51 PDT
Created attachment 467402 [details] [diff] [review]
Split 1b: CssLogic

Rebased patch. There have been some important merges into the default m-c branch. So, these patches need rebase.

Other changes:
- included a fix for bug 588726
- dropped CssLogic.matchesQuery() in favor to element.mozMatchesSelector() - thanks to dbaron for the tip!
Comment 41 Mihai Sucan [:msucan] 2010-08-19 06:38:24 PDT
Created attachment 467403 [details] [diff] [review]
Split 2b: the CssHtmlTree

Rebased patch.
Comment 42 Rob Campbell [:rc] (:robcee) 2010-08-19 07:42:12 PDT
Created attachment 467410 [details] [diff] [review]
Split 2c: The CssHtmlTree (rebased for tree panel integration)

rebased patch based on the tree panel and editor from bug 572038 and bug 575234.
Comment 43 Rob Campbell [:rc] (:robcee) 2010-08-19 07:42:41 PDT
test results from mochitest browser chrome with above patches installed:

    Browser Chrome Test Summary
            Passed: 6874
            Failed: 19
            Todo: 5
     
    *** End BrowserChrome Test Results ***
    TEST-START | Shutdown
    INFO | automation.py | Application ran for: 0:07:25.072400
    INFO | automation.py | Reading PID log: /var/folders/Bv/BvxnJzazGzeR6Yy4QRUEFU+++TI/-Tmp-/tmpIN7vmOpidlog
    WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!
     
    INFO | runtests.py | Running tests: end.
    mochitest-browser-chrome failed:
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_cssinfo_order.js | Inspector Tree Panel is open
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_cssinfo_order.js | Timed out
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_cssinfo_order.js | Found a tab after previous test timed out: data:text/html,<!DOCTYPE%20html><html><head><style>%20.test,%20#test%20{%20color:%20green%20}%20div%20{%20color:%20blue%20}%20</style></head><body><div%20class='test'>cssInfo.rules%20order%20test%20for%20Inspector</div></body></html>
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_domPanel.js | Exception thrown at chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_domPanel.js:89 - SyntaxError: missing : after property id
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_iframeTest.js | Timed out
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_iframeTest.js | Found a tab after previous test timed out: data:text/html,iframe%20tests%20for%20inspector
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_scrolling.js | Timed out
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_scrolling.js | Found a tab after previous test timed out: data:text/html,mouse%20scrolling%20test%20for%20inspector
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_stylepanel_groupview.js | Inspector Tree Panel is open
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_stylepanel_groupview.js | Timed out
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/browser/base/content/test/browser_inspector_stylepanel_groupview.js | Found a tab after previous test timed out: data:text/html,<!DOCTYPE%20html><html><head><style>%20.test,%20#test%20{%20color:%20green%20}%20div%20{%20color:%20blue%20}%20</style></head><body><div%20class='test'>cssInfo.rules%20order%20test%20for%20Inspector</div></body></html>
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js | the network nodes are hidden when the corresponding filter is switched off - Got 5, expected 0
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js | the network nodes are hidden when the search string is set to "hxxp" - Got 5, expected 0
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js | the network nodes are hidden when the search string is set to " zzzz   zzzz " - Got 5, expected 0
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js | the network nodes are hidden when searching for weasels - Got 5, expected 0
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js | the network nodes are hidden when searching for the bell character - Got 5, expected 0
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js | the network nodes are hidden when searching for the string "foo" - Got 5, expected 0
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js | the network nodes are hidden when searching for the string 'foo' - Got 5, expected 0
    TEST-UNEXPECTED-FAIL | chrome://mochikit/content/browser/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js | the network nodes are hidden when searching for the string "foo"bar'baz"boo'" - Got 5, expected 0
Comment 44 Mihai Sucan [:msucan] 2010-08-19 11:37:30 PDT
Created attachment 467471 [details] [diff] [review]
Split 1c: CssLogic

Updating the patches, once again. It has been decided that:

- bug 587125 gets merged.
- and this bug will depend on bug 572038 from now.

Sorry for this high-traffic of patch updates.
Comment 45 Mihai Sucan [:msucan] 2010-08-19 11:40:17 PDT
Created attachment 467476 [details] [diff] [review]
Split 2d: the CssHtmlTree

Changes:

- rebased patch on top of bug 572038.
- merged bug 587125.
- more test code fixes - those that relate to the stylePanel. Other WebConsole and Inspector tests still fail, but they are unrelated.
Comment 46 Dietrich Ayala (:dietrich) 2010-08-22 08:22:12 PDT
This would be an _awesome_ improvement. However, not going to hold the release for it. If you get a well-tested patch ready in time, ask for approval.
Comment 47 Kevin Dangoor 2010-08-23 07:42:10 PDT
I agree that this patch is awesome, but I think it goes beyond that. A couple weeks ago, I put this together to solidify my thinking about the value of the Inspector:

http://hg.mozilla.org/users/kdangoor_mozilla.com/dtplanning/raw-file/tip/fx4/Inspector.html

The top item is this style panel. This is the feature that differentiates this Inspector from those of WebKit and Firebug. It's the first view of styles  that I've seen that directly exposes to the user how the browser figures out what an element should look like rather than just dumping out a whole bunch of information and then letting the user make sense of it all.

I really want to see this land. Dolske had planned to get to the review late last week (but hasn't yet done so). I'm worried that the blocker "-" will mean that this won't make it, thus removing a ton of value from the Inspector. Obviously, there are a lot of strings in this patch so I fear not landing this in b5 means not landing it at all.

It's also worth noting that Joe, Mihai, Rob and Patrick have all to one degree or another been doing work around this patch. So it has had an uncommon amount of attention in our group.

I'm re-requesting blocking status for this.
Comment 48 Dietrich Ayala (:dietrich) 2010-08-23 08:22:43 PDT
Thanks for the context. If this change is what ultimately differentiates this feature, than it should block the release (or the shipping of the Inspector) - blocking+.
Comment 49 Axel Hecht [:Pike] 2010-08-23 08:31:10 PDT
Making this betaN+ sounds far fetched, it has l10n impact, and it's arguably a feature, too. I'd argue that this needs to make feature and l10n freeze if it's supposed to be in, which is beta5+, right?
Comment 50 Dietrich Ayala (:dietrich) 2010-08-23 08:49:44 PDT
Yes, Axel's correct, and Kevin is aware of that as the deadline for this change (per comment 47) -> blocking b5+
Comment 51 Justin Dolske [:Dolske] 2010-08-24 19:08:28 PDT
Comment on attachment 467471 [details] [diff] [review]
Split 1c: CssLogic

A few immediate comments, haven't looked at the guts in detail yet:

>+++ b/browser/base/content/browser.js
...
>+#include csslogic.js

Why browser.js? I think this is only intended for use in the Inspector, so it ought to be stuck in there. I would say just plop csslogic.js directly into inspector.js. Or make it a .jsm and Cu.import() it.


>+CssLogic.STATUS = {
>+  BEST: 3,
>+  MATCHED: 2,
>+  PARENT_MATCH: 1,
>+  UNMATCHED: 0,
>+  UNKNOWN: -1,
>+};

Set these directly in the prototype?

>+CssLogic.STATUS_NAMES = [
>+  //"Unmatched", "Parent Match", "Matched", "Best Match"
>+];

Ditto.

>+CssLogic.cacheStatusNames = function CssLogic_cacheStatusNames(aStrings)
>+{

Not sure why this 1 function isn't a method on the prototype?

>+CssLogic.prototype = {
...
>+        if (domSheet.disabled) {
>+          continue;
>+        }

General nit: 1-line blocks should omit braces (unless the other block of an if/else pair is multiline, in which both should be braced).

This will be a huge win for CssSpecificity.compareTo(). :)
Comment 52 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-25 01:02:14 PDT
Thanks for the comments.

We totally agree about csslogic being best as a .jsm. We're want to do some performance work on it, and we're planning to make it a .jsm at that point.

I put things like .STATUS etc on the constructor rather than on the prototype so you could access them easily without creating an instance (or using .prototype.)

Point taken about brackets - will fix.

I will also have a patch update that merges some changes from elsewhere to make things easier for you.
Comment 53 Mihai Sucan [:msucan] 2010-08-25 03:04:05 PDT
(In reply to comment #51)
> Comment on attachment 467471 [details] [diff] [review]
> Split 1c: CssLogic
> 
> A few immediate comments, haven't looked at the guts in detail yet:
> 
> >+++ b/browser/base/content/browser.js
> ...
> >+#include csslogic.js
> 
> Why browser.js? I think this is only intended for use in the Inspector, so it
> ought to be stuck in there. I would say just plop csslogic.js directly into
> inspector.js. Or make it a .jsm and Cu.import() it.

Agreed. This has been discussed and we'll work on this soon. Not sure what priority is assigned to this change, but we have it mind, hehe.


> >+CssLogic.STATUS = {
> >+  BEST: 3,
> >+  MATCHED: 2,
> >+  PARENT_MATCH: 1,
> >+  UNMATCHED: 0,
> >+  UNKNOWN: -1,
> >+};
> 
> Set these directly in the prototype?
> 
> >+CssLogic.STATUS_NAMES = [
> >+  //"Unmatched", "Parent Match", "Matched", "Best Match"
> >+];
> 
> Ditto.

Thought of this, but these are static properties, unrelated to any object instance.


> >+CssLogic.cacheStatusNames = function CssLogic_cacheStatusNames(aStrings)
> >+{
> 
> Not sure why this 1 function isn't a method on the prototype?

Same as above.

> >+CssLogic.prototype = {
> ...
> >+        if (domSheet.disabled) {
> >+          continue;
> >+        }
> 
> General nit: 1-line blocks should omit braces (unless the other block of an
> if/else pair is multiline, in which both should be braced).
> 
> This will be a huge win for CssSpecificity.compareTo(). :)

True, but I followed the coding style from MDC, which very clearly states:

"Always brace controlled statements, even a single-line consequent of an if else else. This is redundant typically, but it avoids dangling else bugs, so it's safer at scale than fine-tuning."

See https://developer.mozilla.org/En/Mozilla_Coding_Style_Guide#Control_Structures

Most of all code I saw uses the braces for one-liners. Also, I'd add that I wanted to keep the same coding style as throughout the Inspector and the WebConsole.

I'd favor keeping the braces, however I leave the final decision to you.

Thank you very much for your review!
Comment 54 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-25 05:34:44 PDT
Created attachment 469021 [details] [diff] [review]
Split 1d: CssLogic

Merging in patches from other sources, and some clean-up.
Comment 55 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-25 05:35:42 PDT
Created attachment 469022 [details] [diff] [review]
Split 2e: the CssHtmlTree

Latest version incorporating patches from other sources
Comment 56 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-25 06:15:10 PDT
Created attachment 469032 [details] [diff] [review]
Split 1e: CssLogic

Previous upload had some changes to csslogic in csshtmltree. Fixed
Comment 57 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-25 06:18:34 PDT
Created attachment 469033 [details] [diff] [review]
Split 1f: CssLogic

Fixing bizarre cut-off from patch
Comment 58 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-25 06:20:28 PDT
Created attachment 469034 [details] [diff] [review]
Split 2f: the CssHtmlTree
Comment 59 Mihai Sucan [:msucan] 2010-08-25 08:00:20 PDT
Created attachment 469057 [details] [diff] [review]
Split 2g: the CssHtmlTree

Rebased patch on top of the latest tree panel patches.
Comment 60 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-25 10:19:31 PDT
Created attachment 469088 [details] [diff] [review]
Split 1g: CssLogic

More patch merging.
Comment 61 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-25 10:20:23 PDT
Created attachment 469089 [details] [diff] [review]
Split 2h: the CssHtmlTree

More patch merging
Comment 62 Mihai Sucan [:msucan] 2010-08-25 13:44:05 PDT
Created attachment 469169 [details] [diff] [review]
Split 1h: CssLogic

Apologies for the numerous patch updates.

This set of patch updates comes with fixes for regressions: CssLogic failed to find the best match. This issue is now fixed in this patch.
Comment 63 Mihai Sucan [:msucan] 2010-08-25 13:51:01 PDT
Created attachment 469174 [details] [diff] [review]
Split 2i: the CssHtmlTree

Patch updates:

- regression fix: property view failed to expand when there were more than 5 unmatched rules.
- regression fix: the browser_inspector_stylePanel.js test file changes went missing in the last patch update, which meant that the test failed to execute with the new Style Panel code. Updated the patch to include the required changes, such that all tests pass.
- the "Show rule score" input failed to remember its state when you highlighted a new node.
- merged the "Text" and "Fonts and Color" property groups, as Limi suggested.
- other minor changes.

Thanks!
Comment 64 Justin Dolske [:Dolske] 2010-08-25 17:19:48 PDT
(In reply to comment #53)

> > >+#include csslogic.js
> > 
> > Why browser.js? I think this is only intended for use in the Inspector, so it
> > ought to be stuck in there. I would say just plop csslogic.js directly into
> > inspector.js. Or make it a .jsm and Cu.import() it.
> 
> Agreed. This has been discussed and we'll work on this soon. Not sure what
> priority is assigned to this change, but we have it mind, hehe.

Good you've thought of it, but this does need to change for the current patch. If not making it a JSM now, it should be #included in inspector.js instead.


> True, but I followed the coding style from MDC, which very clearly states:

Sigh. That guide has never really reflected current practices for JS code, especially in /browser.

I do think changing at least CssSpecificity.compareTo() would be worthwhile, though it's not a huge problem.
Comment 65 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-26 10:50:27 PDT
Created attachment 469518 [details] [diff] [review]
Split 1i: CssLogic

- rebased on top of the latest tree panel patches (bug 572038) and on top of the latest default mozilla-central branch (as of today).

- merged changes requested by dolske

- fixed some misleading comments

- made a fix for media=all. On ubuntu.com the style panel did not show any rule, from any stylesheet.

- made a fix for tab switching - the "show rule score" lost its state across tab switches.
Comment 66 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-26 10:51:09 PDT
Created attachment 469519 [details] [diff] [review]
Split 2j: the CssHtmlTree
Comment 67 Justin Dolske [:Dolske] 2010-08-26 15:58:17 PDT
Comment on attachment 469169 [details] [diff] [review]
Split 1h: CssLogic

I was about 3/4 of the way through reviewing 1h before you posted the updated 1i. So, it's possible some of these comments no longer apply. Update 1i with the below and we'll see where things are at then.


>+/**
>+ * Other bits of CSS display logic work simply by digging into the actually

"actual".

>+function CssLogic(aStylePanel, aStrings)
>+{

It's weird to be passing in a string bundle here. Just make this a memoizing getter in the prototype. (Or if you end up making it a JSM now, it can be a XPCOMUtils.defineLazyGetter in the JSM's scope, to avoid it being per-instance).


>+CssLogic.prototype = {
...
>+  highlight: function CssLogic_highlight(aViewedElement)
...
>+    this.viewedElement = aViewedElement;
>+    let doc = this.viewedElement.ownerDocument;
>+    if (doc != this.viewedDocument) {
>+      // New document: clear/rebuild the cache.
>+      this.viewedDocument = doc;
>+
>+      this.sheets = [];

Hmm, so, you're retaining references to elements and documents, but I don't see them explicitly released anywhere. Have to looked at the lifetimes of your objects, to ensure we're not keeping junk alive longer than it needs to be?

>+      // Hunt down top level stylesheets, and cache them.
>+      let domSheets = this.viewedDocument.styleSheets;

What if a page dynamically adds stylesheets after you've created this static cache?

>+        if (domSheet.disabled) {
>+          continue;
>+        }

...or if a page toggles .disabled? Or uses .insertRule() / .deleteRule()?

>+    try {
>+      let win = this.viewedDocument.defaultView;
>+      this._computedStyle = win.getComputedStyle(this.viewedElement, "");
>+    } catch (ex) {
>+      Services.console.logStringMessage("Warning getComputedStyle errored " +
>+        "for " + this.viewedElement + ". " + ex);
>+    }

When does this actually throw?


>+  _cacheSheet: function CssLogic_cacheSheet(aDomSheet)
...
>+        // If it is an @import rule, it will have a styleSheet property.
>+        if (domRule.styleSheet) {

if (domRule.type == CSSRule.IMPORT_RULE)

>+    this.sheets.push(new CssSheet(this.viewedDocument, aDomSheet));

Can't the CssSheet just derive the document by way of aDomSheet.ownerNode.ownerDocument? That would be a slightly simpler API that's harder to accidentally screw up / get into an undefined state.

CSS2.1 says @imports must occur before everything else (except @charset), so it would be a nice perf boost to bail out of the loop here when the first non-@import, non-@charset is encountered.


>+CssLogic.getShortName = function CssLogic_getShortName(aElement)
>+{
...
>+  return aElement.tagName + "[" + priorSiblings + "]";

Seems like this might be a little confusing. Makes me think of XPath, but not quite.


>+CssLogic.getShortNamePath = function CssLogic_getShortNamePath(aElement)
>+{
>+  let doc = aElement.ownerDocument;
>+  let reply = [];
>+
>+  // Work up to document.body, but don't include it.
>+  while (aElement && aElement != doc.body && aElement != doc.documentElement) {
>+    reply.unshift({
>+      display: CssLogic.getShortName(aElement),
>+      element: aElement
>+    });
>+    aElement = aElement.parentNode;
>+  }
>+
>+  // If we don't have anything then include document.body or whatever element
>+  // is.
>+  if (aElement && !reply.length) {
>+    reply.unshift({
>+      display: CssLogic.getShortName(aElement),
>+      element: aElement
>+    });
>+  }

Rephrased, this is "always include aElement, and then include any interesting path above it if present".

So this should be simpler as:

  if (!aElement) return []; // impossible?
  do {
    reply.unshift({
      display: CssLogic.getShortName(aElement),
      element: aElement
    });
    aElement = aElement.parentNode;
  } while(aElement && aElement != doc.body && aElement != doc.documentElement)



>+CssSheet.prototype = {
...
>+  get href()
>+  {
>+    if (!this._href) {
>+      try {

When can this throw? This and the other generic try/catch blocks should be avoided unless they're actually needed (or it's likely that the code inside is at risk of throwing).

>+        this._href = this._domSheet.href;
>+        if (!this._href) {
>+          this._href = this._document.defaultView.location.href;

this._href = this._document.location;

>+  get shortSource()
...
>+        // Get rid of any query string. There are times when this might be the
>+        // wrong thing to do, but getting rid of it seems like the simplest option
>+        // so we should do that until we have a good reason to do otherwise.
>+        let queryPos = this._source.indexOf("?");
>+        if (queryPos != -1) {

Consider http://site.com/?makeSomeCSS

Maybe not worth worrying about for now? Followup bug?

Might consider converting into a nsIURI [Services.io.newURI(this._source, null, null)], and accessing just the parts you want that way?


>+  get domRules()
>+  {
>+    if (!this._domRules) {
>+      // Cross domain sheets give NS_ERROR_DOM_SECURITY_ERR

Really? You're running as chrome, no? Seems like that shouldn't happen.

>+  /**
>+   * Dig through all the sheets looking for matches for <tt>property</tt> and
>+   * adding the results to <tt>rules</tt>. This method also finds the properties
>+   * that are defined inline (element.style).
>+   */
>+  _findRules: function CssInfo_findRules()

I think the comment it wrong, _findRules doesn't seem to look at element.style at all (but findElementRules does).


>+  _markMatches: function CssInfo_markMatches()
>+  {

It might be worthwhile to have someone like dbaron take a look at this function, since you're sorta reimplementing part of CSS here. Would be good for catching any quirky corner cases...


>+  // domUtils.getRuleLine fails for @import rules.

File a bug for this (and note it here), seems like it shouldn't!

>+  this.line = -1;
>+  if (aCssLogic.stylePanel && this.selector != "@element.style") {
>+    try {
>+      this.line = aCssLogic.stylePanel.domUtils.getRuleLine(aDomRule);

If sheet's an inline <style>, is the line number here relative to the document source, or start of the <style> block?



>+function CssSpecificity(aSelector, aIndex, aPriority)
>+{

This looks like another good chunk of code to ask dbaron for feedback on.

>+    aSelector.split(/[ >+]/).forEach(function(aSimple) {

Shouldn't "~" should be here too?

>+      // The regex leaves empty nodes combinators like ' > '
>+      if (!aSimple) {
>+        return;
>+      }
>+      // See section 6.4.3 -> http://www.w3.org/TR/CSS2/cascade.html#specificity

Should this instead reference (and implement) http://www.w3.org/TR/css3-selectors/#specificity

>+      this.ids += (aSimple.match(/#/g) || []).length;

/^#/ to force matching at the beginning? (consider ".weird#class"?)

I'm not sure if I should go through the CSS grammar to looks for edge cases here and the rest of the function. Have you done so?


>+CssSpecificity.prototype = {
...
>+  compareTo: function CssSpecificity_compareTo(aThat)
>+  {

Thrice for having a CSS expert look at this function.
Comment 68 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-26 16:54:23 PDT
It's late (or should that be early) so I'm going to do a quick fly-through of your points. Anything I don't mention, I agree with and we'll work on it.

You are correct about there being a general issue with caching. In the short-term, we need the performance boost, but we've got a plan optimizing our lookups, and I think this will alleviate the need for the caching. Is it OK for us to fix these issues with bug 589849?

> CSS2.1 says @imports must occur before everything else (except @charset),
> so it would be a nice perf boost to bail out of the loop here when the first
> non-@import, non-@charset is encountered.

It looks to me that the browser doesn't actually ignore @imports that come later on. So we might we be technically correct, but practically wrong by doing this?

> >+  return aElement.tagName + "[" + priorSiblings + "]";
> Seems like this might be a little confusing. Makes me think of XPath,
> but not quite.

You are right, however we are really stuck for space here. We are waiting for limi to report on UI issues. Perhaps he should have some input.

> > _markMatches() 
>
> It might be worthwhile to have someone like dbaron take a look at this
> function, since you're sorta reimplementing part of CSS here. Would be good
> for catching any quirky corner cases...

Good point. I think this function will be cut in half by our performance work,
so if it's OK with you, I'd like to postpone this until we've done that.

> >+function CssSpecificity(aSelector, aIndex, aPriority)
>
> This looks like another good chunk of code to ask dbaron for feedback on.

We already know of a bug in this function, and Mihai has done some great work in bug 590090 fixing it. We should get dbaron's feedback when were more sure that it is bug free. Specifically I think you've probably found some issues for us to look at.

I'd like to raise some bugs for some of these issues so we can fix them properly rather than rushing something wrong in. Is that OK?

Thanks for the review. Hugely useful.

Joe.
Comment 69 Justin Dolske [:Dolske] 2010-08-26 17:08:31 PDT
(In reply to comment #68)

> You are correct about there being a general issue with caching. ...
> Is it OK for us to fix these issues with bug 589849?

Yes.

> It looks to me that the browser doesn't actually ignore @imports that come
> later on.

Hmpf, ok, ignore me then!

> > > _markMatches() 
> >
> > It might be worthwhile to have someone like dbaron take a look at this
> > function, since you're sorta reimplementing part of CSS here. Would be good
> > for catching any quirky corner cases...
> 
> Good point. I think this function will be cut in half by our performance work,
> so if it's OK with you, I'd like to postpone this until we've done that.

That's fine, but file a followup bug so we don't lose track of the issue. It should, at worst, be mostly-working as is, so that's fine for initial user feedback. But as a quality developer tool, we should obviously make sure it's not doing things wrong and giving people misleading info.

> I'd like to raise some bugs for some of these issues so we can fix them
> properly rather than rushing something wrong in. Is that OK?

Yes, I'd be fine with filing followup bugs for the things I've noted so far (though for the safe, minor stuff it's probably easier to just fix them, but your call).

Please mark them as blocking this bug.
Comment 70 Patrick Walton (:pcwalton) 2010-08-26 18:03:23 PDT
Created attachment 469719 [details] [diff] [review]
Split 1j: CssLogic

Patch rebased to trunk.
Comment 71 Patrick Walton (:pcwalton) 2010-08-26 18:18:34 PDT
Created attachment 469725 [details] [diff] [review]
Split 2k: the CssHtmlTree

Patch rebased to trunk.
Comment 72 Axel Hecht [:Pike] 2010-08-27 03:53:25 PDT
(In reply to comment #36)
> Ehsan and Axel: thank you for your suggestions for improving the
> internationalization of the Style panel.
> 
> I'd like to make the changes you have requested, but I still have some
> questions:
> 
> 1. Do I reuse an existing DTD or do I create a new one?  If I have to create a
> new one, should I put it in the same location as browser.dtd? If not, to which
> DTD should I add the new strings?

A new DTD file would be good, next to inspector.properties.

> 2. As you can see in csshtmltree.html, the HTML templates have almost no actual
> strings, they mostly have placeholders like ${variable}. Those are generated
> dynamically by the csshtmltree.js script. Some of those variables are strings
> that need localization. Do I use inspector.properties for those, or do I ensure
> from the JS that I pass entity names that are added into the HTML?

inspector.properties sounds right for those.

A few more comments:

Why is style.property.important localizable? Isn't that just CSS?
MDC is MDN now, I think.
Nit on localization notes, 
# LOCALIZATION NOTE (style.rule.sourceUnknown,style.rule.sourceElement,style.rule.sourceInline):
I didn't get what style.rule.specificity is about, fwiw. What are those 4 variables?
Comment 73 Kevin Dangoor 2010-08-27 06:13:03 PDT
reducing the depends list, because this bug does not truly depend on those followups.
Comment 74 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-27 07:16:42 PDT
I'm just going through things making fixes, and raising bugs for stuff that
we're not going to fix today.

Anything I've missed out is fixed.

> >+CssLogic.prototype = {
> ...
> >+  highlight: function CssLogic_highlight(aViewedElement)
> ...
> >+    this.viewedElement = aViewedElement;
> >+      this.viewedDocument = doc;
> >+      this.sheets = [];
> 
> Hmm so you're retaining references to elements and documents, but I don't see
> them explicitly released anywhere. Have to looked at the lifetimes of your
> objects, to ensure we're not keeping junk alive longer than it needs to be?

The idea is that inspector.js calls highlight(null) which releases them.
I think this is done correctly, but we should check. I have raised bug 591211
to ensure that we do this.


> >+      // Hunt down top level stylesheets, and cache them.
> >+      let domSheets = this.viewedDocument.styleSheets;
>
> What if a page dynamically adds stylesheets after you've created this static
> cache?
>
> >+        if (domSheet.disabled) {
> >+          continue;
> >+        }
>
> ...or if a page toggles .disabled? Or uses .insertRule() / .deleteRule()?

I have raised bug 591212 which will make sure that we do this as part of the
performance work


> >+  get shortSource()
> ...
> >+  // Get rid of any query string. There are times when this might be the
> >+  // wrong thing to do, but getting rid of it seems like the simplest option
> >+  // so we should do that until we have a good reason to do otherwise.
> >+  let queryPos = this._source.indexOf("?");
> >+  if (queryPos != -1) {
>
> Consider http://site.com/?makeSomeCSS
>
> Maybe not worth worrying about for now? Followup bug?

Simple fix: "if (queryPos > 0)" ensures that the string has substance.


> >+  // domUtils.getRuleLine fails for @import rules.
>
> File a bug for this (and note it here), seems like it shouldn't!

See bug 591303


> >+  this.line = -1;
> >+  if (aCssLogic.stylePanel && this.selector != "@element.style") {
> >+    try {
> >+      this.line = aCssLogic.stylePanel.domUtils.getRuleLine(aDomRule);
>
> If sheet's an inline <style>, is the line number here relative to the document
> source, or start of the <style> block?

line numer in the HTML document, which is what we want.


> >+    aSelector.split(/[ >+]/).forEach(function(aSimple) {
> 
> Shouldn't "~" should be here too?

Yes, but it's not that simple because of selectors like E[foo~="bar"]
This will be fixed as part of bug 590090.


> >+      this.ids += (aSimple.match(/#/g) || []).length;
> 
> /^#/ to force matching at the beginning? (consider ".weird#class"?)
> 
> I'm not sure if I should go through the CSS grammar to looks for edge cases
> here and the rest of the function. Have you done so?

We think that irrespective of what the spec says, the browser looks up elements
with class="weird" and id="class" here, but we'll check. We will also double
check with the spec as part of this. See bug 591308
Comment 75 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-27 08:56:49 PDT
Created attachment 469890 [details] [diff] [review]
Split 1j: CssLogic

Fixes as requested by dolske + a rebase.
Comment 76 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-27 09:07:49 PDT
Created attachment 469894 [details] [diff] [review]
Split 2l: the CssHtmlTree

Rebase and minor change (due to parameter change) to keep up with review changes to csslogic.
Comment 77 Mihai Sucan [:msucan] 2010-08-27 09:09:54 PDT
(In reply to comment #72)
> (In reply to comment #36)
> > Ehsan and Axel: thank you for your suggestions for improving the
> > internationalization of the Style panel.
> > 
> > I'd like to make the changes you have requested, but I still have some
> > questions:
> > 
> > 1. Do I reuse an existing DTD or do I create a new one?  If I have to create a
> > new one, should I put it in the same location as browser.dtd? If not, to which
> > DTD should I add the new strings?
> 
> A new DTD file would be good, next to inspector.properties.
> 
> > 2. As you can see in csshtmltree.html, the HTML templates have almost no actual
> > strings, they mostly have placeholders like ${variable}. Those are generated
> > dynamically by the csshtmltree.js script. Some of those variables are strings
> > that need localization. Do I use inspector.properties for those, or do I ensure
> > from the JS that I pass entity names that are added into the HTML?
> 
> inspector.properties sounds right for those.
> 
> A few more comments:
> 
> Why is style.property.important localizable? Isn't that just CSS?
> MDC is MDN now, I think.
> Nit on localization notes, 
> # LOCALIZATION NOTE
> (style.rule.sourceUnknown,style.rule.sourceElement,style.rule.sourceInline):
> I didn't get what style.rule.specificity is about, fwiw. What are those 4
> variables?

Thanks for your reply, Axel!

The Style panel uses all of its strings from JavaScript, and as you said it's appropriate for us to use inspector.properties. Therefore, at the moment we do not need a new DTD. I have made the other changes to the internationalization code, as you have suggested back then.

Joe will change the string from MDC to MDN in the upcoming CssHtmlTree patch.

With regards to the four localization strings (the four sources): I tried to explain it in the notes... Maybe I was unsuccessful - sorry. The idea is that we show style properties that are each grouped into style rules. Rules are given by selectors - they are rules on how to match elements in the web page (HTML). Developers can put such style rules with properties in multiple locations - inside the web page, and external to the page, in CSS files. Those style.rule.source* strings refer to different types of such sources. When the external source can be determined - a file or some URL - we show it, instead of any localized string.

Maybe Joe can improve the localization note for the four strings. I am not a native English speaker and I already tried, hehe. :)

I did add the style.property.important string because the idea is not to display the CSS source which has "!important", but to display a human-readable string that tells the developer the style property is marked as important, inside the style rule. While I agree that in English this is not really needed - CSS itself is English - I think that people from other countries won't mind seeing this localized.

If you consider that string is unnecessary, we can remove it.

Thanks again!
Comment 78 Justin Dolske [:Dolske] 2010-08-27 17:24:25 PDT
Comment on attachment 469894 [details] [diff] [review]
Split 2l: the CssHtmlTree

(Partial review, posting what I have done so far. More coming.)

>+++ b/browser/base/content/browser.xul
...
>-        <listbox id="inspector-style-listbox" flex="1"/>
>+        <browser id="inspector-style-browser"

Why a <browser>? Can you just use an <iframe> here? Seems like that's all that's really needed.


>+++ b/browser/base/content/csshtmltree.js
...
>+function CssHtmlTree(aStyleWin, aCssLogic, aStrings)
>+{

As in the other file, just make a this.strings getter, instead of passing it in through the constructor.

>+  // 'window' in that it contains a document

TBH, I don't think that any of the comments in the constructor here really add value. I'd just mention csshtmltree.xhtml once and nuke all the others, that way it's easy to tell it's just a big block of getElementById() calls.




>+  _template: function CssHtmlTree_template(aTemplate, aDestination, aData)
>+  {
...
>+    (new Templater()).processNode(duplicated, aData);

Seems like all of Templater could just be made static (this, just |Templater.processNode(x,y)| here)?


>+  createStyleGroupViews: function CssHtmlTree_createStyleGroupViews()
>+  {
>+    // Take care if you want to change the names of the group titles, these
>+    // strings are used in internationalization, so the changes need to be
>+    // followed through to the nsIStringBundle of the Inspector
>+    // (see inspector.properties).
>+    this.styleGroups = [
>+      new StyleGroupView(this, "Text, Fonts and Color", [

Pass in the actual ID here (something like "textFontColorGroup"), instead of the an english string that then gets magically mangled into an ID.

>+      new StyleGroupView(this, "Effects and Other", [

For a followup bug:

I think you really need a way to dynamically get a list of all known-to-Gecko CSS properties (which I believe are common to all elements?), group the ones this code knows about, and then plop everything else into this "Other" group. Alternatively, write a test that looks at these tables and fails when an new (unknown) property is added.

The problem here is that no one is going to remember to modify this code when new CSS properties are added in the future. So, either a test is needed to ensure people know to do that, or new properties should automatically show up somewhere generic (at which point users would presumably notice and file a bug to clean up the UI).


>+StyleGroupView.prototype = {
>+  /**
>+   * The click event handler for the title of the style group view.
>+   */
>+  click: function StyleGroupView_click()
>+  {
...
>+    if (!this.closed) {
>+      this.element.style.display = "none";
>+      this.toggle.innerHTML = "&#x25B6;";

Eww.

This should just be done with CSS. Set an attribute (|open="true"|, like XUL), and have 1 structural rule that controls .display, and 1 theme rule that controls the twisty widget appearance.

We've got native-looking twisties for Windows in toolkit/themes/winstripe/global/tree/, not sure where the OS X ones are coming from. There's also toolkit/themes/*/global/arrow/ which is uglier but on all platforms. Maybe just use those for now, and file a theme bug to sort out where to get nice arrows for any platform.


>+function PropertyView(aTree, aGroup, aName)
>+{
...
>+  this.link = "https://developer.mozilla.org/en/CSS/" + aName;

This needs to either be localized, or use a language-agnostic link (which the site would presumably redirect to as appropriate).

Not sure how to do this on DevMo; https://developer.mozilla.org/en/CSS/color works bug https://developer.mozilla.org/CSS/color just redirects to the toplevel page. File a followup to sort this out?


>+PropertyView.prototype = {
...
>+  click: function PropertyView_click(aEvent)
>+  {
...
>+    if (!this.closed) {
>+      this.element.style.display = "none";
>+      if (this.toggle) {
>+        this.toggle.innerHTML = "&#x25B6;";
>+      }

Ditto for using CSS and chrome:// images here.


>+  _populateTemplate: function PropertyView_populateTemplate(showAll)

Checkpoint: I'm going to skip the rest of this file for the moment, and jump ahead to some other stuff.


>+++ b/browser/base/content/csshtmltree.xhtml
>@@ -0,0 +1,284 @@
>+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
>+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

HTML5 is the new XHTML!

>+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">

The xml:lang is going to be wrong for most locales!

>+  <!-- The title is localized when the Style panel is open by inspector.js -->
>+  <title>Style</title>

Just leave it blank, then, or use a string entity if you want a fallback string.

>+  <style type="text/css">
>+    body {
>+      font-family: Lucida Grande, sans-serif;

All this should go into separate CSS files. browser/base/content/foo.css for structural stuff, and browser/themes/*/browser/bar.css for presentational stuff (ie, stuff that themes would want to control).


>+    /* Take away these two :visited rules to get a core dumper */

???


>+  <div id="templateHeader">
>+    <p class="path">
>+      <label>${l10n('lookingAtLabel')}</label>
>+      <ol>
>+        <li foreach="item in ${pathElements}">
>+          <a href="#" onclick="${pathClick}"

Templated JS makes me afraid of security issues. Can this just be made a constant |onclick="foo(this)"|, where foo(aElement) looks at aElement / aElement.getAttribute("something") to get the data it needs?

>+        <input type="checkbox" onchange="${toggleSpecificity}"

Ditto.

>+      <a class="link" href="https://developer.mozilla.org/en/CSS/Specificity"

Also needs to deal with localization.


I generally wonder if inventing a new template format is worthwhile here, seems like it might be more straightforward to just have the code look for the specific sections (<div> IDs, say) and use DOM createElement brute force to create what's needed.



>+++ b/browser/locales/en-US/chrome/browser/inspector.properties
...
>+style.property.numberOfRulesSingular=%1$S rule
>+style.property.numberOfRulesPlural=%1$S rules

Singular and Plural? Oh, if only it was so simple!

See https://developer.mozilla.org/en/Localization_and_Plurals

You'll be wanting to fix this in a followup. :)
Comment 79 Rob Campbell [:rc] (:robcee) 2010-08-27 19:13:48 PDT
> This should just be done with CSS. Set an attribute (|open="true"|, like XUL),
> and have 1 structural rule that controls .display, and 1 theme rule that
> controls the twisty widget appearance.
> 
> We've got native-looking twisties for Windows in
> toolkit/themes/winstripe/global/tree/, not sure where the OS X ones are coming
> from. There's also toolkit/themes/*/global/arrow/ which is uglier but on all
> platforms. Maybe just use those for now, and file a theme bug to sort out where
> to get nice arrows for any platform.

gavin suggested | -moz-appearance: treetwistyopen | for mac and linux and it seems to work fine.

I've had mixed results with the toolkit/themes/winstripe/global/tree graphics but they should do.
Comment 80 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-28 02:12:17 PDT
This is the list of things from comment 78 that we're going to try fix quickly

- Use <iframe> rather than <browser> if we can
- Do l10n through a getter rather than passing a strings blob in
- Reduce clutter in the constructor
- Pass an titleID into the StyleGroupView ctor rather than en_US to be mangled
- Use CSS rather than hacking innerHTML (twice)
- Replace xhtml with html5 (check with l10n team!)
- Don't specify the lang in the <html> element (or localize it!)
- Leave the <title> blank at the start
- Move the CSS into a separate file
Comment 81 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-28 02:12:50 PDT
This is the list of things from comment 78 that we have opened bugs for:

> Seems like all of Templater could just be made static (this, just
> |Templater.processNode(x,y)| here)?

and:

> I generally wonder if inventing a new template format is worthwhile here,
> seems like it might be more straightforward to just have the code look for
> the specific sections (<div> IDs, say) and use DOM createElement brute force
> to create what's needed.

We have an open issue as to the best way to do templating in general. This
template engine was taken from Bespin, because we wanted something simple but
functional. You are right that this could be static, but there didn't seem to
be much point in making the change when we're probably going to make bigger
changes down the line.

As to the issue of whether we need a template engine at all: Some of the
complexity is perhaps masked by the fact that we've got 3 levels of nested
templates. Converting them all to straight DOM would be a significant bloat,
and I'm convinced that if we have a template engine, that it should be used
here. Standing alone it may not be worth templating, but I think there is a
bigger question-mark to be answered.

Bug 591580


> I think you really need a way to dynamically get a list of all known-to-Gecko
> CSS properties (which I believe are common to all elements?), group the ones
> this code knows about, and then plop everything else into this "Other" group.
> Alternatively, write a test that looks at these tables and fails when an new
> (unknown) property is added.
>
> The problem here is that no one is going to remember to modify this code when
> new CSS properties are added in the future. So, either a test is needed to
> ensure people know to do that, or new properties should automatically show up
> somewhere generic (at which point users would presumably notice and file a bug
> to clean up the UI).

Bug 591582


> >+  this.link = "https://developer.mozilla.org/en/CSS/" + aName;
>
> This needs to either be localized, or use a language-agnostic link (which the
> site would presumably redirect to as appropriate).

Bug 591584


> >+ /* Take away these two :visited rules to get a core dumper */

We did have a core dumper, I will be checking to see if it still exists and
raising a bug if it does.


> Singular and Plural? Oh, if only it was so simple!
>
> See https://developer.mozilla.org/en/Localization_and_Plurals

Bug 591590
Comment 82 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-28 02:14:29 PDT
One other thing to understand better:

> >+  <div id="templateHeader">
> >+    <p class="path">
> >+      <label>${l10n('lookingAtLabel')}</label>
> >+      <ol>
> >+        <li foreach="item in ${pathElements}">
> >+          <a href="#" onclick="${pathClick}"
> 
> Templated JS makes me afraid of security issues. Can this just be made a
> constant |onclick="foo(this)"|, where foo(aElement) looks at aElement /
> aElement.getAttribute("something") to get the data it needs?
> 
> >+        <input type="checkbox" onchange="${toggleSpecificity}"
> 
> Ditto.

Could you clarify what the security issues could be?
Thanks,
Comment 83 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-28 02:15:46 PDT
I should add: thanks for the review. I've just been through it quickly so we can get started. More comments as we learn more.
Comment 84 Axel Hecht [:Pike] 2010-08-28 04:52:07 PDT
PS: There's no l10n technology for HTML5, I don't think it's up to the task of writing localizable applications.
Comment 85 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-28 10:29:09 PDT
(In reply to comment #84)
> PS: There's no l10n technology for HTML5, I don't think it's up to the task of
> writing localizable applications.

We do have localization - it's provided by the templating, so I think this is a bit of a bikeshed issue. I lean slightly towards HTML5 because it's the hip new thing, but really I'd like to get everyone to consensus, and the patch in.
Comment 86 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-28 11:01:06 PDT
I previously put this down to do quickly:
- "Use CSS rather than hacking innerHTML (twice)"

On reflection, we are going to do them in a separate bug because it's not as easy as I thought, and because we've yet to hear finally from limi on this issue.

Bug 591651
Comment 87 Mihai Sucan [:msucan] 2010-08-28 12:45:31 PDT
(In reply to comment #84)
> PS: There's no l10n technology for HTML5, I don't think it's up to the task of
> writing localizable applications.

From a technical perspective localization in HTML5 versus XHTML is the same. I have switched the code from .html to .xhtml as was recommended by you.

Dolske and Joe: I would suggest we keep csshtmltree.xhtml and not rename back to .html. I will modify the DOCTYPE to be that of HTML5: <!DOCTYPE html>. Doctypes have no relevance other than triggering the standards-mode rendering.
Comment 88 Axel Hecht [:Pike] 2010-08-28 14:26:07 PDT
I'll obviously need to open a thread on .platform on this.
Comment 89 Mihai Sucan [:msucan] 2010-08-29 07:02:04 PDT
(In reply to comment #78)
> Comment on attachment 469894 [details] [diff] [review]
> Split 2l: the CssHtmlTree
> 
> (Partial review, posting what I have done so far. More coming.)
> 
> >+++ b/browser/base/content/browser.xul
> ...
> >-        <listbox id="inspector-style-listbox" flex="1"/>
> >+        <browser id="inspector-style-browser"
> 
> Why a <browser>? Can you just use an <iframe> here? Seems like that's all
> that's really needed.

Good point. Using an <iframe> now. Works fine with that as well.

> >+++ b/browser/base/content/csshtmltree.js
> ...
> >+function CssHtmlTree(aStyleWin, aCssLogic, aStrings)
> >+{
> 
> As in the other file, just make a this.strings getter, instead of passing it in
> through the constructor.

Good point. Implemented the same solution as was done by Joe in csslogic.js.


> >+  // 'window' in that it contains a document
> 
> TBH, I don't think that any of the comments in the constructor here really add
> value. I'd just mention csshtmltree.xhtml once and nuke all the others, that
> way it's easy to tell it's just a big block of getElementById() calls.

Correct. Changed the constructor now.


> >+  _template: function CssHtmlTree_template(aTemplate, aDestination, aData)
> >+  {
> ...
> >+    (new Templater()).processNode(duplicated, aData);
> 
> Seems like all of Templater could just be made static (this, just
> |Templater.processNode(x,y)| here)?

Good point. The Templater comes from the Bespin project. I have changed it now to use a static object. Simpler and nicer.


> >+  createStyleGroupViews: function CssHtmlTree_createStyleGroupViews()
> >+  {
> >+    // Take care if you want to change the names of the group titles, these
> >+    // strings are used in internationalization, so the changes need to be
> >+    // followed through to the nsIStringBundle of the Inspector
> >+    // (see inspector.properties).
> >+    this.styleGroups = [
> >+      new StyleGroupView(this, "Text, Fonts and Color", [
> 
> Pass in the actual ID here (something like "textFontColorGroup"), instead of
> the an english string that then gets magically mangled into an ID.

Good point. Fixed.


> >+      new StyleGroupView(this, "Effects and Other", [
> 
> For a followup bug:
> 
> I think you really need a way to dynamically get a list of all known-to-Gecko
> CSS properties (which I believe are common to all elements?), group the ones
> this code knows about, and then plop everything else into this "Other" group.
> Alternatively, write a test that looks at these tables and fails when an new
> (unknown) property is added.
> 
> The problem here is that no one is going to remember to modify this code when
> new CSS properties are added in the future. So, either a test is needed to
> ensure people know to do that, or new properties should automatically show up
> somewhere generic (at which point users would presumably notice and file a bug
> to clean up the UI).

Good point. This is for a follow-up. See bug 591582.

Not sure if we have an API for this kind of stuff, or we should auto-magically infer such information using the DOM 2 Style API?


> >+StyleGroupView.prototype = {
> >+  /**
> >+   * The click event handler for the title of the style group view.
> >+   */
> >+  click: function StyleGroupView_click()
> >+  {
> ...
> >+    if (!this.closed) {
> >+      this.element.style.display = "none";
> >+      this.toggle.innerHTML = "&#x25B6;";
> 
> Eww.
> 
> This should just be done with CSS. Set an attribute (|open="true"|, like XUL),
> and have 1 structural rule that controls .display, and 1 theme rule that
> controls the twisty widget appearance.
> 
> We've got native-looking twisties for Windows in
> toolkit/themes/winstripe/global/tree/, not sure where the OS X ones are coming
> from. There's also toolkit/themes/*/global/arrow/ which is uglier but on all
> platforms. Maybe just use those for now, and file a theme bug to sort out where
> to get nice arrows for any platform.

Hehe, good point. I changed the code to use open=true|false, and to change the .display property based on that attribute from CSS - no longer JS for that. Agreed it was ugly.

I haven't switched to XUL twisties and the likes because that kind of work, IIANM, require more cross-platform testing. For the moment, what I did is, I hope, sufficient to go past review.

Nonetheless, Joe has reported bug 591651 as a follow-up, if we further need to make improvements to the code.


> >+function PropertyView(aTree, aGroup, aName)
> >+{
> ...
> >+  this.link = "https://developer.mozilla.org/en/CSS/" + aName;
> 
> This needs to either be localized, or use a language-agnostic link (which the
> site would presumably redirect to as appropriate).
> 
> Not sure how to do this on DevMo; https://developer.mozilla.org/en/CSS/color
> works bug https://developer.mozilla.org/CSS/color just redirects to the
> toplevel page. File a followup to sort this out?

Yeah, Joe has filled bug 591584. Currently, I cannot do anything about this issue. We can't link to supposedly-localized pages for each CSS property, until MDN properly redirects to the English version.


> >+++ b/browser/base/content/csshtmltree.xhtml
> >@@ -0,0 +1,284 @@
> >+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
> >+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
> 
> HTML5 is the new XHTML!

Hehe, yeah. I switched to the HTML5 DOCTYPE: <!DOCTYPE html>. However, I kept the file as XHTML because this was asked by Axel - first iterations of the Style panel code used an .html file.


> >+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
> 
> The xml:lang is going to be wrong for most locales!

Agreed. Removed.

> >+  <!-- The title is localized when the Style panel is open by inspector.js -->
> >+  <title>Style</title>
> 
> Just leave it blank, then, or use a string entity if you want a fallback
> string.

Left it blank. It's dynamically localized by the Inspector code.


> >+  <style type="text/css">
> >+    body {
> >+      font-family: Lucida Grande, sans-serif;
> 
> All this should go into separate CSS files. browser/base/content/foo.css for
> structural stuff, and browser/themes/*/browser/bar.css for presentational stuff
> (ie, stuff that themes would want to control).

Agreed. I moved the style into browser/base/content/csshtmltree.css. There will be, perhaps, substantial changes post-beta5 for the Style panel UI, based on feedback from Limi.

I have not made the separation between theme and content style, because I am not even sure how to do it, and it's all very much subject to change. Additionally, I think the style will need a review, a cleanup after beta5 - once we implement Limi's UI changes.


> >+  <div id="templateHeader">
> >+    <p class="path">
> >+      <label>${l10n('lookingAtLabel')}</label>
> >+      <ol>
> >+        <li foreach="item in ${pathElements}">
> >+          <a href="#" onclick="${pathClick}"
> 
> Templated JS makes me afraid of security issues. Can this just be made a
> constant |onclick="foo(this)"|, where foo(aElement) looks at aElement /
> aElement.getAttribute("something") to get the data it needs?

True, but here we do not execute any user-code at all. I don't see any potential security issue. Please expand.


> I generally wonder if inventing a new template format is worthwhile here, seems
> like it might be more straightforward to just have the code look for the
> specific sections (<div> IDs, say) and use DOM createElement brute force to
> create what's needed.

Agreed. This has been previously discussed within the devtools team. Please see bug 591580.


> >+++ b/browser/locales/en-US/chrome/browser/inspector.properties
> ...
> >+style.property.numberOfRulesSingular=%1$S rule
> >+style.property.numberOfRulesPlural=%1$S rules
> 
> Singular and Plural? Oh, if only it was so simple!
> 
> See https://developer.mozilla.org/en/Localization_and_Plurals
> 
> You'll be wanting to fix this in a followup. :)

Agreed. Joe has filed bug 591590. However, today I fixed this issue - I hope it's fine now.

Thank you very much for your time to review these style panel patches!

(will attach the updated patch in a few moments)
Comment 90 Axel Hecht [:Pike] 2010-08-29 07:05:14 PDT
FTR, we're feature and string frozen for B5, this should land for B6.

I still disagree with the l10n aspects of this code, but I don't have the cycles to reverse engineer the whole thing.
Comment 91 Mihai Sucan [:msucan] 2010-08-29 07:10:16 PDT
Created attachment 470291 [details] [diff] [review]
Split 2m: the CssHtmlTree

Updated patch with changes requested by Dolske in his latest review. (see previous comment)

Other minor changes:
- Renamed the Bullets style group to Lists.
- Moved the Lists group to be the second group displayed in the style panel.
(these two changes were suggested by Limi)

Also note that plurals are now used for more than the "numberOfRules" l10n string. We had other places where we could've made use of plurals.

Thanks again for your review!
Comment 92 Axel Hecht [:Pike] 2010-08-29 07:23:13 PDT
(In reply to comment #87)
> From a technical perspective localization in HTML5 versus XHTML is the same.

That's completely wrong.
Comment 93 Mihai Sucan [:msucan] 2010-08-29 07:33:23 PDT
(In reply to comment #92)
> (In reply to comment #87)
> > From a technical perspective localization in HTML5 versus XHTML is the same.
> 
> That's completely wrong.

Oops. Sorry. Then I am not aware of how localization works, but I am happy to learn. I thought only from the perspective of entities: we can use entities both in HTML and XHTML. (or maybe I am wrong with this as well!)

With regards to the localization of the style panel: please let us know what further changes we need to make. Thank you very much!
Comment 94 Axel Hecht [:Pike] 2010-08-29 07:43:37 PDT
We only parse DTDs for xhtml, not for html files, so we can't use entities in html.

As for figuring out how to move content from .properties to .dtd, strings that are dynamically generated should probably be in the .properties, whereas strings that are conditionally shown can be in the DTD and be switched on and off via CSS. The most extreme example of such a code would be netError.xhtml, http://mxr.mozilla.org/mozilla-central/source/docshell/resources/content/netError.xhtml.
Comment 95 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-29 08:13:18 PDT
(In reply to comment #90)
> FTR, we're feature and string frozen for B5, this should land for B6.
> 
> I still disagree with the l10n aspects of this code, but I don't have the
> cycles to reverse engineer the whole thing.

It looks like string freeze is September 10, shipped with beta 6 not beta 5.
https://wiki.mozilla.org/Firefox/4/Beta


(In reply to comment #94)
Is there something in the handling of .properties and .dtd which means we should prefer localization via .dtd?
Comment 96 Axel Hecht [:Pike] 2010-08-29 08:20:24 PDT
We have string and feature freezes for B5, which are done. And for Firefox proper, which have been moved to B6. My comment was merely to make sure we don't have more feature work landing over the weekend.

Re dtd vs properties, yes, DTDs are in general preferred as it's easier to find how and why which strings show up where in the UI. With .properties, you basically have to reverse engineer the code every time.
Comment 97 Kevin Dangoor 2010-08-29 10:19:18 PDT
Re-requesting blocking status because we need to incorporate Axel's feedback into this feature and land it as soon as possible in b6.
Comment 98 Mihai Sucan [:msucan] 2010-08-29 10:25:47 PDT
Created attachment 470313 [details] [diff] [review]
Split 2n: the CssHtmlTree

Updated patch.

Moved the strings that we could move into a new inspector.dtd file (from the inspector.properties file), as recommended by Axel (thank you!). The XHTML file now includes the inspector.dtd entities.

Axel: please check and let us know if there are further internationalization changes needed. Thanks a lot!
Comment 99 Axel Hecht [:Pike] 2010-08-29 13:45:54 PDT
Comment on attachment 470313 [details] [diff] [review]
Split 2n: the CssHtmlTree

Noting a review request on myself, I'll have more comments tomorrow.
Comment 100 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-08-30 04:26:41 PDT
(In reply to comment #96)
> Re dtd vs properties, yes, DTDs are in general preferred as it's easier to find
> how and why which strings show up where in the UI. With .properties, you
> basically have to reverse engineer the code every time.

I understand that we need to write code for the .properties case, but from your point of view, to work out how it is used, doesn't it come down to a 'grep -R' in both cases:

$ time grep -Rn 'inspectStylePanelTitle.label' .
./base/content/browser.xul:296: ...
./locales/en-US/chrome/browser/browser.dtd:193: ...
grep -Rn 'inspectStylePanelTitle.label' .  0.03s user 0.03s system 93% cpu 0.065

$ time grep -Rn 'specificityHelpLink' .        
./base/content/csshtmltree.xhtml:208: ...
./locales/en-US/chrome/browser/inspector.properties:28: ...
./locales/en-US/chrome/browser/inspector.properties:31: ...
grep -Rn 'specificityHelpLink' .  0.03s user 0.03s system 89% cpu 0.068 total

We've done the work, so this isn't about that, I'm trying to understand why this is important.
Thanks,
Comment 101 Axel Hecht [:Pike] 2010-08-30 04:44:47 PDT
For example, style.rule.sourceUnknown won't find a call site. I don't even find sourceUnknown outside of the properties files. OK, are they actually used?

Trying an example where I can come up with something, style.rule.status.BEST. That's a partial match in cacheStatusNames, and depends on CssLogic.STATUS. That's likely in csslogic.js, which I can't find.

This turns out to be tricky to explain, cause I don't find the call sites for the strings I pick.
Comment 102 Axel Hecht [:Pike] 2010-08-30 05:28:16 PDT
Ahh, csslogic is in the other patch in this bug. You probably want a comment cross referencing the STATUS array to the localized names, to make sure people understand that the two need to be consistent.

Strings that are not yet used but are planned to be added in follow ups should be added in those patches.

Localization Notes should reference the IDs they refer to as single explicit list, comma separated. So instead of 

# LOCALIZATION NOTE (style.rule.sourceUnknown):
# LOCALIZATION NOTE (style.rule.sourceElement):
# LOCALIZATION NOTE (style.rule.sourceInline):

make that 

# LOCALIZATION NOTE (style.rule.sourceUnknown, style.rule.sourceElement, style.rule.sourceInline):
(Though I expect these strings to move to another patch)

style.rule.specificity.* entries with Plurals should each get a PluralForm comment.

I wish the path from style.group. to an <h1> was simpler. I wonder if those could be all in <span>s with a class and CSS to switch one of them on. Like
<h1 onclick=${group.click}" class="${tbd}">
 <span class="Text_Fonts_and_Color">&style.group.Text_Fonts_and_Color;</span>
 <span class="Background">&style.group.Background;</span>
 ...
</h1>
Comment 103 Mihai Sucan [:msucan] 2010-08-30 11:18:40 PDT
Created attachment 470489 [details] [diff] [review]
Split 1k: CssLogic

Updated patch. Only changed the comment for the CssLogic.STATUS array, as requested by Axel.
Comment 104 Mihai Sucan [:msucan] 2010-08-30 11:23:39 PDT
Created attachment 470493 [details] [diff] [review]
Split 2o: the CssHtmlTree

Updated patch. Changes:

- removed unused strings. This means I'll update bug 585565 accordingly.
- made changes to LOCALIZATION NOTES as requested (including mentioning of PluralForms).
- made changes to groups, such that the group titles show up in the csshtmltree.xhtml file (and in the new inspector.dtd). This required a bit of changes in csshtmltree.js, but nothing dramatic / not too much.

Thanks for your review Axel! Please let us know if further changes are required.
Comment 105 Axel Hecht [:Pike] 2010-08-30 16:23:30 PDT
Comment on attachment 470493 [details] [diff] [review]
Split 2o: the CssHtmlTree

That looks pretty cool, thanks.

I'd have one nit, could you replicate the plurals comment for each entity? I'm having an upcoming patch to compare-locales that will read those, and it'll be much easier if I can just do that per entity.
Comment 106 Axel Hecht [:Pike] 2010-09-01 09:18:54 PDT
Comment on attachment 470493 [details] [diff] [review]
Split 2o: the CssHtmlTree

PS: Canceling the review request in favor of the feedback flag.
Comment 107 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-09-02 04:54:29 PDT
If you are having trouble applying the csshtmltree patch then it's because it needs rebasing on top of the html tree panel (bug 572038).

I've had some difficulty getting those patches to work correctly, so I'm hesitating before I upload a rebased patch. If you find a reject with inspector.js, to do with highlight(null), then try this patchlet instead:

diff --git a/browser/base/content/inspector.js b/browser/base/content/inspector.js
--- a/browser/base/content/inspector.js
+++ b/browser/base/content/inspector.js
@@ -782,6 +782,9 @@
     if (this.isStylePanelOpen) {
       this.stylePanel.hidePopup();
     }
+    this.cssLogic.highlight(null);
+    this.cssHtmlTree.highlight(null);
+
     if (this.domPanel) {
       this.domPanel.hidePopup();
       this.domBox = null;

Any confirmation of the issue, and I'll upload a rebased patch.
Comment 108 Justin Dolske [:Dolske] 2010-09-03 02:45:18 PDT
Comment on attachment 470489 [details] [diff] [review]
Split 1k: CssLogic

First, apologies for taking so long to get back to this.

I have a few tiny comments on the current patch, but I'm going to go ahead an mark it r+.

I think this code is adequate to land, although I still have some significant concerns about its correctness. Specifically:

  * Handling dynamic page style updates (comment 67) -- bug 591212
  * Behavior of CssSpecificity / markMatches() / smaller nits can differ from what the browser's style system actually does. -- bug 592743
  * Getting a consult from a resident CSS expect for any other corner cases I've missed.

The second worries me the most (a broken tool can be far more frustrating than no tool at all). But I think this is an excellent start, you've already recognized these weak areas, and we'll get important feedback from this as-is while improving it.


>+CssLogic.l10n = function CssLogic_l10n(aName)
>+{
>+  if (!CssLogic._strings) {
>+    CssLogic._strings = Services.strings.createBundle(
>+        "chrome://browser/locale/inspector.properties");
>+  }
>+  return CssLogic._strings.GetStringFromName(aName);
>+};

You could just use XPCOMUtils to handle the memoization...

  XPCOMUtils.defineLazyGetter(CssLogic, "_strings", function() {
    return Services.strings.createBundle(...);
  });

  CssLogic.l10n = function CssLogic_l10n(aName)
  {
    return CssLogic._strings.GetStringFromName(aName);
  };


>+  get shortSource()
>+  {
>+    if (!this._shortSource) {
>+      this._shortSource = this.domSheet.href;
>+      if (this._shortSource) {
>+        this._shortSource = this._shortSource.split("/");
>+        this._shortSource = this._shortSource[this._shortSource.length - 1];

nsIURL might help here, and avoiding the need to parse off any query...

  let url = Services.ios.newURI(this._shortSource, null, null);
  url.QueryInterface(Ci.nsIURL);
  if (url.filename)
    this._shortSource = uri.filename;


>+  get ruleCount()
>+  {
>+    if (!this._rules) {
>+      this._findRules();
>+    }
>+    if (!this._localRules) {
>+      this._findElementRules();

This pattern happens in 2 other places [along with ._matched / ._markMatches()]. Might help to roll these all into a single setup() / init() call.

Is the ruleCount() special case really worth it? Seems like it's just used in one place...

>
>+  _findRules: function CssInfo_findRules()
>+  {
...
>+              if (!domRule.selectorText) {
>+                // See https://bugzilla.mozilla.org/show_bug.cgi?id=591349

"bug 591349" is plenty. :)

>+                // jlog('no selectorText in', domRule);

Nit, nuke.
Comment 109 Justin Dolske [:Dolske] 2010-09-03 03:29:39 PDT
Comment on attachment 470493 [details] [diff] [review]
Split 2o: the CssHtmlTree

A few brief comments before I crash... Will finish up tomorrow, this is top of my list still.

>+++ b/browser/base/content/browser.xul
...
>-        <listbox id="inspector-style-listbox" flex="1"/>
>+        <iframe id="inspector-style-browser" flex="1" tooltip="aHTMLTooltip"
>+                src="chrome://browser/content/csshtmltree.xhtml" />

Hmm, you might need to leave out |src| here, and instead set it at runtime (eg, in an onpanelshowing handler), otherwise this might cause a Ts/Txul regression due to overhead from loading the iframe. Maybe having it in a <panel> avoids the problem, but I'm not sure. Pushing it to try and looking at the perf numbers would answer that. Or it might be easier to just change it. :)


>+++ b/browser/base/content/csshtmltree.css
>+ * The Initial Developer of the Original Code is
>+ * Netscape Communications Corporation.
>+ * Portions created by the Initial Developer are Copyright (C) 1998-1999

Still hoping to ship in Netscape 4.0? :) Please update.

>+body {
>+  font-family: Lucida Grande, sans-serif;
>+  font-size: 11px;
>+  background: #EEE;
>+}

Style rules like these (fonts, colors, gradients, images, etc) should go into platform-specific files under browser/themes/*/browser/, fine to make them identical across platforms for now.

>+.property-view[open="false"] > table {
>+  display: none;
>+}

Functional/structural things like this should stay in this file, though.

Things in between can be a bit fuzzy, the general guideline is that is a 3rd party theme could reasonably be expected to want to do things differently, it should go under browser/themes/*. Style that would break functionality if missing or don't make sense to change stays in browser/base/content.


>+/* Take away these two :visited rules to get a core dumper */

Remove or file, lest this live on forever.

>+.property-view[open="false"] .rule-count:after {
>+  content: " ▶";
>+}
>+.property-view[open="true"] .rule-count:after {
>+  content: " ▼";
>+}

See comment 79.
Comment 110 Justin Dolske [:Dolske] 2010-09-03 03:42:27 PDT
(In reply to comment #81)
> This is the list of things from comment 78 that we have opened bugs for:

> > I generally wonder if inventing a new template format is worthwhile here,
> > seems like it might be more straightforward to just have the code look for
> > the specific sections (<div> IDs, say) and use DOM createElement brute force
> > to create what's needed.
> 
> We have an open issue as to the best way to do templating in general. This
> template engine was taken from Bespin, because we wanted something simple but
> functional.

Fair enough. Good that this wasn't freshly invented, at least. :) I'll look again with that in mind.


(In reply to comment #82)
> > >+        <li foreach="item in ${pathElements}">
> > >+          <a href="#" onclick="${pathClick}"
> > 
> > Templated JS makes me afraid of security issues.
> 
> Could you clarify what the security issues could be?

Basically, all the typical XSS type of concerns. EG, if a page can inject a value like |" onmouseover="doEvilStuff()| here, the user is pwned. This code it all running with chrome privileges, so it needs to be extra sure not to allow that to happen. (It might be interesting to look at running this in a content-type iframe, with some chrome helpers, so that an XSS-style flow wouldn't give access to anything more than a normal webpage would have.)



(In reply to comment #94)
> We only parse DTDs for xhtml, not for html files, so we can't use entities in
> html.

Really? That's surprising, I would have expected that to work. (ISTR the spec says a UA can, but that authors shouldn't assume a UA will). Haven't tried it, but in any case the HTML vs XHTML thing isn't a big deal.
Comment 111 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-09-03 06:06:53 PDT
(In reply to comment #108)
> Comment on attachment 470489 [details] [diff] [review]
> Split 1k: CssLogic
> 
> First, apologies for taking so long to get back to this.
> 
> I have a few tiny comments on the current patch, but I'm going to go ahead an
> mark it r+.
> 
> I think this code is adequate to land, although I still have some significant
> concerns about its correctness. [snip]

Thanks.
We share your concerns for its correctness, and we are attempting to get expert review.

There is a test tool which intrigues me - it does a tree walk on a random page comparing what we predict for a property, and what the computed style is. I'll look into how feasible this is.


> >+CssLogic.l10n = function CssLogic_l10n(aName)
> >+{
> >+  if (!CssLogic._strings) {
> >+    CssLogic._strings = Services.strings.createBundle(
> >+        "chrome://browser/locale/inspector.properties");
> >+  }
> >+  return CssLogic._strings.GetStringFromName(aName);
> >+};
> 
> You could just use XPCOMUtils to handle the memoization...
> 
>   XPCOMUtils.defineLazyGetter(CssLogic, "_strings", function() {
>     return Services.strings.createBundle(...);
>   });
> 
>   CssLogic.l10n = function CssLogic_l10n(aName)
>   {
>     return CssLogic._strings.GetStringFromName(aName);
>   };

I did it this way because I liked that it could all be done in once place with less dependencies, but I'm happy to change it if needs be.


> >+  get shortSource()
> >+  {
> >+    if (!this._shortSource) {
> >+      this._shortSource = this.domSheet.href;
> >+      if (this._shortSource) {
> >+        this._shortSource = this._shortSource.split("/");
> >+        this._shortSource = this._shortSource[this._shortSource.length - 1];
> 
> nsIURL might help here, and avoiding the need to parse off any query...
> 
>   let url = Services.ios.newURI(this._shortSource, null, null);
>   url.QueryInterface(Ci.nsIURL);
>   if (url.filename)
>     this._shortSource = uri.filename;

Done.


> >+  get ruleCount()
> >+  {
> >+    if (!this._rules) {
> >+      this._findRules();
> >+    }
> >+    if (!this._localRules) {
> >+      this._findElementRules();
> 
> This pattern happens in 2 other places [along with ._matched /
> ._markMatches()]. Might help to roll these all into a single setup() / init()
> call.
> 
> Is the ruleCount() special case really worth it? Seems like it's just used in
> one place...

This was all to do with optimizing things for the template engine. However we've got a better way of doing things now as part of the performance work. See the patch in bug 589849 for more details. (This patch has not been internally reviewed right now)


> >+  _findRules: function CssInfo_findRules()
> >+  {
> ...
> >+              if (!domRule.selectorText) {
> >+                // See https://bugzilla.mozilla.org/show_bug.cgi?id=591349
> 
> "bug 591349" is plenty. :)

Done

> >+                // jlog('no selectorText in', domRule);
> 
> Nit, nuke.

Done.
Thanks.
Comment 112 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-09-03 06:21:17 PDT
(In reply to comment #110)
> (In reply to comment #82)
> > > >+        <li foreach="item in ${pathElements}">
> > > >+          <a href="#" onclick="${pathClick}"
> > > 
> > > Templated JS makes me afraid of security issues.
> > 
> > Could you clarify what the security issues could be?
> 
> Basically, all the typical XSS type of concerns. EG, if a page can inject a
> value like |" onmouseover="doEvilStuff()| here, the user is pwned. This code
> is all running with chrome privileges, so it needs to be extra sure not to
> allow that to happen. (It might be interesting to look at running this in a
> content-type iframe, with some chrome helpers, so that an XSS-style flow
> wouldn't give access to anything more than a normal webpage would have.)

We are better off here than is obvious because we're not doing conventional
string templating - this is DOM templating - we're manipulating the DOM rather
than strings. Clearly, calling setAttribute('" onmouseover="doEvilStuff()')
should not cause a security problem.
Comment 113 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-09-03 07:29:57 PDT
Created attachment 471837 [details] [diff] [review]
Split 1l: CssLogic

This patch fixes the minor issues raised by dolske. It retains it's r+ status.
Comment 114 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-09-03 10:26:44 PDT
Created attachment 471903 [details] [diff] [review]
Split 1m: CssLogic

fixing a merge issue. no content change
Comment 115 Kevin Dangoor 2010-09-03 19:57:10 PDT
Inspector feature postponed. Removing blocking flags from Inspector bugs.
Comment 116 Kevin Dangoor 2010-09-03 20:35:43 PDT
Removing items from kd4b6.
Comment 117 Mihai Sucan [:msucan] 2010-09-04 13:10:20 PDT
Created attachment 472189 [details] [diff] [review]
Split 1n: CssLogic

Fixed regression. The CssSheet.shortSource getter failed to work - there were some typos (Services.ios versus Services.io, uri versus url, etc).
Comment 118 Kevin Dangoor 2010-09-04 19:00:41 PDT
Reprioritizing bugs. You can filter the mail on the word TEABAGS.
Comment 119 Justin Dolske [:Dolske] 2010-09-07 23:30:30 PDT
Comment on attachment 470493 [details] [diff] [review]
Split 2o: the CssHtmlTree

>+++ b/browser/base/content/browser.xul
...
>-        <listbox id="inspector-style-listbox" flex="1"/>
>+        <iframe id="inspector-style-browser" flex="1" tooltip="aHTMLTooltip"
>+                src="chrome://browser/content/csshtmltree.xhtml" />

Still wary about this possibly causing a perf regression, as a minimum this needs to run through try server and have the perf numbers examined.


>+++ b/browser/base/content/csshtmltree.css
...
>+body {
>+  font-family: Lucida Grande, sans-serif;
>+  font-size: 11px;
>+  background: #EEE;
>+}

Split structural/presentational CSS.... Oh, this is the same patch version as I started reviewing in comment 109. So I'll just finish up with the parts I didn't look at there!

>+++ b/browser/base/content/csshtmltree.js
...
>+/**
>+ * A quick job scheduler to allow us to do lots of work without choking the
>+ * main thread.
>+ * TODO: There must be a better way to not choke the main thread.
>+ * @see bug 587760.
>+ */

This is kind of sadmaking, we really should look at what the slow parts are here and see if we can those faster. Especially because this limits you to 10 jobs/second, so that could lead to problems with the user thinking the UI is broken, only to have things pop into view seconds later.


>+Templater.processNode = function Templater_processNode(aNode, aData)
>+{

Sigh. Templates.

I'm really not keen on this at all, for the aforementioned security risks, and its a blob of relatively complex code unlike anything else we use in the browser. I suspect it's break-even, code wise, with just doing plain old JS-driven DOM creation without any templates (and loses in the long run due to maintenance burden). And then there's documentation and tests... Might be better to just use XUL <tree>s here, since it's an existing UI widget that does most of what you need.

I'd like Gavin to weigh in here.

[Since this is going to be pref'd off for Fx4, I suppose there is the option of landing as-is (more or less), as long as we can all agree on what the final architecture should looks like and when it needs to be done...]


>+/**
>+ * Like eval, but that creates a context of the variables in
>+ * <tt>aEnvironment</tt> in which the script is evaluated.
>+ * WARNING: This script uses 'with' which is generally regarded to be evil.
>+ * The alternative is to create a Function at runtime that takes X parameters
>+ * according to the X keys in the aEnvironment object, and then call that
>+ * function using the values in the aEnvironment object. This is likely to be
>+ * slow, but workable.

Blah. Does the new ES5 Function.bind() help out here?

http://whereswalden.com/2010/09/07/now-in-spidermonkey-and-firefox-es5s-function-prototype-bind/
Comment 120 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-09-08 01:06:02 PDT
(In reply to comment #119)
> Comment on attachment 470493 [details] [diff] [review]

> >+++ b/browser/base/content/csshtmltree.js
> ...
> >+/**
> >+ * A quick job scheduler to allow us to do lots of work without choking the
> >+ * main thread.
> >+ * TODO: There must be a better way to not choke the main thread.
> >+ * @see bug 587760.
> >+ */
> 
> This is kind of sadmaking, we really should look at what the slow parts are
> here and see if we can those faster. Especially because this limits you to 10
> jobs/second, so that could lead to problems with the user thinking the UI is
> broken, only to have things pop into view seconds later.

Thanks to Mihai, we already have a patch for this. See bugs 589849 and 587760


> >+Templater.processNode = function Templater_processNode(aNode, aData)
> >+{
> 
> Sigh. Templates.
> 
> I'm really not keen on this at all, for the aforementioned security risks,

See my earlier comment:
This isn't string templating, it doesn't have the risks associated with string templating. Instead it has similar risks to long-hand DOM coding.


> and
> its a blob of relatively complex code unlike anything else we use in the
> browser. I suspect it's break-even, code wise, with just doing plain old
> JS-driven DOM creation without any templates (and loses in the long run due to
> maintenance burden).

I think that template systems have a valid use in the developers toolbox, and there is more than just this code that wants to use a template engine. As previously stated, this might not be it though.


> Might be better to just use XUL <tree>s here, since it's an existing UI
> widget that does most of what you need.

We don't think so. We had a DOM tree implementation previously. It was much more code and was very ugly from a UI perspective.


> >+/**
> >+ * Like eval, but that creates a context of the variables in
> >+ * <tt>aEnvironment</tt> in which the script is evaluated.
> >+ * WARNING: This script uses 'with' which is generally regarded to be evil.
> >+ * The alternative is to create a Function at runtime that takes X params
> >+ * according to the X keys in the aEnvironment object, and then call that
> >+ * function using the values in the aEnvironment object. This is likely to
> >+ * be slow, but workable.
> 
> Blah. Does the new ES5 Function.bind() help out here?

I don't see how it can. Could you elucidate?

As far as I can see the arguments against using 'with' don't apply here, and it's much easier to understand than the alternatives like 'new Function'.

Thanks
Comment 121 Rob Campbell [:rc] (:robcee) 2010-09-08 11:38:48 PDT
(In reply to comment #119)
> Comment on attachment 470493 [details] [diff] [review]
> Split 2o: the CssHtmlTree
> 
> >+++ b/browser/base/content/browser.xul
> ...
> >-        <listbox id="inspector-style-listbox" flex="1"/>
> >+        <iframe id="inspector-style-browser" flex="1" tooltip="aHTMLTooltip"
> >+                src="chrome://browser/content/csshtmltree.xhtml" />
> 
> Still wary about this possibly causing a perf regression, as a minimum this
> needs to run through try server and have the perf numbers examined.

I've been wrangling with a 1-3% (depending on noise and error) Txul regression from the tree-panel. We're pretty sure it's a result of embedding the iframe in browser.xul. My most-recent patch pulled out the iframe entirely and builds it up after startup using js.

see bug 592525 for details.
Comment 122 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-09-20 04:48:31 PDT
Created attachment 476772 [details] [diff] [review]
Split 1: CssLogic. Upload 16

Merge of several patches into one
Comment 123 Joe Walker [:jwalker] (needinfo me or ping on irc) 2010-09-20 04:50:53 PDT
Created attachment 476773 [details] [diff] [review]
Split 2: HtmlTree. Upload 16

Merge of several patches into one
Comment 124 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-02 04:36:30 PDT
Created attachment 529448 [details] [diff] [review]
CSSLogic + HtmlTree Upload 17

Here is an updated patch for review.
Comment 125 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-02 04:43:37 PDT
Created attachment 529450 [details] [diff] [review]
Test only patch that fully implements jlog and jprofile (upload 3)

Updated patch
Comment 126 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-02 13:12:46 PDT
Comment on attachment 529448 [details] [diff] [review]
CSSLogic + HtmlTree Upload 17

Thanks for agreeing to review this Mihai.
Comment 127 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-02 13:15:47 PDT
Comment on attachment 529450 [details] [diff] [review]
Test only patch that fully implements jlog and jprofile (upload 3)

Thanks for agreeing to do this review Mihai.
Comment 128 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-03 03:59:59 PDT
Created attachment 529670 [details] [diff] [review]
529448: CSSLogic + HtmlTree Upload 18

Noticed a couple of bugs prior to review, fixed
Comment 129 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-03 04:01:19 PDT
Created attachment 529671 [details] [diff] [review]
Test only patch that fully implements jlog and jprofile (upload 4)
Comment 130 Mihai Sucan [:msucan] 2011-05-05 11:02:44 PDT
Comment on attachment 529670 [details] [diff] [review]
529448: CSSLogic + HtmlTree Upload 18

Can you please provide us with an interdiff? One that shows the changes you made since the last patch version that was submitted. Thank you!

Reviewing the entire patch is a huge task, but it's also something I should not do. There's a big chunk of it that I and Joe wrote last summer. It feels like reviewing my own code. :)

So, please attach an interdiff so I can review your work, to see how the changes you did fit.

Preliminary comments:

- the style group views fail after STR:

1. highlight element A
2. open a group view and a property view.
3. switch to element B.
4. the group view and the property view close (as expected).
5. click again to open the previously open group. it fails.

(this is an old bug that I remember was valid in the summer, I know I fixed it once... uh oh)

- some of the inspector tests fail to run, see browser/base/content/test/. you need to fix the tests, or fix the main patches, to make the tests run properly. (it really depends if they found real bugs ... or they just expect some old API)

Thanks you! Sorry for the delay in getting to review this patch.
Comment 131 Mihai Sucan [:msucan] 2011-05-05 11:04:38 PDT
Also, please note that the patch fails to apply cleanly in the devtools repo. We should work on top of the devtools repo, IIANM. Thanks!

(luckily, rebasing was easy!)
Comment 132 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-06 07:28:19 PDT
Created attachment 530612 [details] [diff] [review]
Test only patch that fully implements jlog and jprofile (upload 5)
Comment 133 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-06 07:29:56 PDT
Created attachment 530613 [details] [diff] [review]
Interdiff of jlog and jprofile diffs
Comment 134 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-06 07:31:33 PDT
Created attachment 530614 [details] [diff] [review]
CSSLogic + HtmlTree Upload 19
Comment 135 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-06 07:32:42 PDT
Created attachment 530615 [details]
Htmltree patch comparison in HTML format
Comment 136 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-06 07:41:36 PDT
Mihai, please don't test using the Inspector because this code will not be properly integrated until the highlighter is ready. I have added a command to the Web Console that makes it possible to test the Style Inspector in isolation (almost).

As an example from about:home you could use:
inspectstyle(document.body)
inspectstyle($('brandStartLogo'))

The patches:

Test only patch that fully implements jlog and jprofile (upload 5)
https://bugzilla.mozilla.org/attachment.cgi?id=530612

CSSLogic + HtmlTree Upload 19
https://bugzilla.mozilla.org/attachment.cgi?id=530614


The interdiff and HTML file comparison:

Interdiff of jlog and jprofile diffs
https://bugzilla.mozilla.org/attachment.cgi?id=530613

Htmltree patch comparison in HTML format
https://bugzilla.mozilla.org/attachment.cgi?id=530615

I hope this helps.
Comment 137 Mihai Sucan [:msucan] 2011-05-11 09:41:49 PDT
Comment on attachment 530613 [details] [diff] [review]
Interdiff of jlog and jprofile diffs

This patch is fine, but I don't think we want this in the browser. Do we? We had this code here just for debugging purposes. I don't mind having this stuff in the browser codebase, but I didn't know this was planned. Please ask a browser peer.
Comment 138 Mihai Sucan [:msucan] 2011-05-11 11:36:17 PDT
Comment on attachment 530614 [details] [diff] [review]
CSSLogic + HtmlTree Upload 19

Review of attachment 530614 [details] [diff] [review]:
-----------------------------------------------------------------

The patch looks fine. Thanks for the diffs that show your changes!

Going to review mostly only your changes, based on the HTML diff you attached.

There are some changes needed to be made to the patch. Giving it r- for the moment.

Please also attach a diff showing the changes you made to csslogic. Unfortunately, the diff you attached shows csslogic.js as a new file entirely. I couldn't review your changes there, since I don't know the new stuff in the code.

::: browser/base/content/browser.xul
@@ +287,5 @@
>             noautohide="true"
>             titlebar="normal"
> +           close="true"
> +           label="&inspectStylePanelTitle.label;"
> +           onpopuphiding="this.hidden = true;">

You do not need to do this. Whenever you need to check the panel state use panel.state.

See:

https://developer.mozilla.org/en/XUL/panel#p-state

@@ +289,5 @@
> +           close="true"
> +           label="&inspectStylePanelTitle.label;"
> +           onpopuphiding="this.hidden = true;">
> +        <iframe id="inspector-style-browser" flex="1" tooltip="aHTMLTooltip"
> +                src="chrome://browser/content/csshtmltree.xhtml" />

For performance reasons please remove the iframe, and load csshtmltree.xhtml only on demand, when the style panel opens up.

We had problems with this before.

::: browser/base/content/csshtmltree.js
@@ +160,5 @@
> +        if (refId && window.HUDService.hudReferences[refId] && elt) {
> +          window.HUDService.hudReferences[refId].jsterm.sandbox.inspectstyle(elt);
> +        }
> +      } 
> +    }

I think you should provide an API to allow the style panel users to hook into the pathClick event. I don't consider ideal that you have Web Console and Inspector code here. This stuff should live in their own places.

@@ +554,5 @@
> +      "-moz-user-input",          // inherit http://www.w3.org/TR/2000/WD-css3-userint-20000216#user-input
> +      "-moz-user-modify",         // inherit http://www.w3.org/TR/2000/WD-css3-userint-20000216#user-modify
> +      "-moz-user-select",         // no      http://www.w3.org/TR/2000/WD-css3-userint-20000216#user-select
> +      "-moz-window-shadow",       //
> +    ];

I think it would be better to put this list of properties and groups into a single variable.

Even better: put them in the static, non-instanced, of the CssHtmlTree object.

CssHtmlTree.propertiesByGroup or something.

@@ +567,5 @@
> +        if (isNaN(parseInt(prop, 10))) {
> +          break;
> +        }
> +        StyleGroupView.styleLookup[styles[prop]] = true;
> +      }

It would be better to do:

for (let i = 0; i < styles.length; i++) {
  let prop = styles.item(i);
  // ...
}

@@ +577,5 @@
> +      let mergedArray;
> +
> +      if (!mergedArray) {
> +        mergedArray = Array.concat(textArr, listArr, backgroundArr, dimsArr, posArr, borderArr, otherArr);
> +      }

You have let mergedArray; which is followed by the check. The mergedArray array is always reconstructed, each time in the loop, for each cssProp. The result is never cached.

@@ +590,5 @@
> +    // If the newCSSProps Array is ever not empty at this point we
> +    // need to move the properties into their appropriate category
> +    newCSSProps.forEach(function(prop) {
> +      otherArr.push(prop);
> +    });

I would like the above code optimized for performance and brevity. Please merge the groups into one array only once, iterate only once through the ComputedCSSStyleDeclaration object returned by getComputedStyle(), and add the new props in the Other group, on-the-fly, in the same run.

@@ +624,5 @@
> +  this.localName = CssHtmlTree.l10n("style.group." + this.id);
> +
> +  this.propertyViews = [];
> +  aPropertyNames.forEach(function(aPropertyName) {
> +    if (this.isPropertySupported(aPropertyName)) {

Why do we need this check?

I believe this can be removed. All properties we have are supported. If we add props that are not supported, it's our error and we need to know that. Otherwise, properties that are added dynamically ... are obviously supported.

@@ +701,5 @@
> +   * @return {boolean} true or false
> +   */
> +  isPropertySupported: function(aProperty) {
> +    return aProperty && StyleGroupView.styleLookup[aProperty];
> +  },

See the comment above.

::: browser/base/content/csshtmltree.xhtml
@@ +62,5 @@
> +        <span class="property-name">
> +          <a class="link" target="_blank" title="&style.property.helpLinkTitle;"
> +              href="${property.link}">${property.name}</a>
> +        </span>
> +        <span class="property-value">&quot;${property.value}&quot;</span>

I am not sure if we want quotes around the property value. We should have quotes only around those values that are of type string in CSS, but not for color keywords, for rgb(), rgba(), and so on. Definitely not for background, etc.

It would be simpler to just not add quotes at all. ;)

::: toolkit/components/console/hudservice/HUDService.jsm
@@ +4350,5 @@
> +    let stylePanel = doc.getElementById("inspector-style-panel");
> +    let cssLogic = new doc.defaultView.CssLogic();
> +    let cssHtmlTree = new doc.defaultView.CssHtmlTree(styleWin, cssLogic);
> +
> +    if (stylePanel.hidden) {

This is not the correct way to check if a panel is open or not. Please see:

https://developer.mozilla.org/en/XUL/panel#p-state

@@ +4358,5 @@
> +      stylePanel.hidden = false;
> +      // open at top right of browser panel, offset by 20px from top.
> +      stylePanel.openPopup(browser, "end_before", 0, 20, false, false);
> +      // size panel to 200px wide by half browser height - 60.
> +      stylePanel.sizeTo(200, win.outerHeight / 2 - 60);

I am not sure if we really want this location. I found it awkward where the style panel opened up. Please also ask Robert about this. I think I'd prefer the style panel to open on top of the Web Console, or something, or even at the location of the node being inspected. The latter would probably be the best choice.
Comment 139 Mihai Sucan [:msucan] 2011-05-11 11:48:44 PDT
I did the diff between the old csslogic.js and the new csslogic.js myself.

Result: r+ on that code. The changes are fine there, nice cleanup. Thanks!
Comment 140 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-19 05:52:08 PDT
Created attachment 533615 [details]
New Htmltree patch comparison in HTML format

Htmltree patch comparison in HTML format (interdiff not possible)
Comment 141 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-19 06:07:09 PDT
Created attachment 533623 [details] [diff] [review]
CSSLogic + HtmlTree Upload 20

::: browser/base/content/browser.xul
> @@ +287,5 @@
> >             noautohide="true"
> >             titlebar="normal"
> > +           close="true"
> > +           label="&inspectStylePanelTitle.label;"
> > +           onpopuphiding="this.hidden = true;">
> 
> You do not need to do this. Whenever you need to check the panel state use panel.state.
> 
> See:
> 
> https://developer.mozilla.org/en/XUL/panel#p-state
> 

Agreed, I thought this was strange myself, Done.

> @@ +289,5 @@
> > +           close="true"
> > +           label="&inspectStylePanelTitle.label;"
> > +           onpopuphiding="this.hidden = true;">
> > +        <iframe id="inspector-style-browser" flex="1" tooltip="aHTMLTooltip"
> > +                src="chrome://browser/content/csshtmltree.xhtml" />
> 
> For performance reasons please remove the iframe, and load csshtmltree.xhtml only on demand, when the style panel opens up.
> 
> We had problems with this before.
> 

Done

> ::: browser/base/content/csshtmltree.js
> @@ +160,5 @@
> > +        if (refId && window.HUDService.hudReferences[refId] && elt) {
> > +          window.HUDService.hudReferences[refId].jsterm.sandbox.inspectstyle(elt);
> > +        }
> > +      } 
> > +    }
> 
> I think you should provide an API to allow the style panel users to hook into the pathClick event. I don't consider ideal that you have Web Console and Inspector code here. This stuff should live in their own places.
> 

A styleInspector object makes a lot of sense, Done.

> @@ +554,5 @@
> > +      "-moz-user-input",          // inherit http://www.w3.org/TR/2000/WD-css3-userint-20000216#user-input
> > +      "-moz-user-modify",         // inherit http://www.w3.org/TR/2000/WD-css3-userint-20000216#user-modify
> > +      "-moz-user-select",         // no      http://www.w3.org/TR/2000/WD-css3-userint-20000216#user-select
> > +      "-moz-window-shadow",       //
> > +    ];
> 
> I think it would be better to put this list of properties and groups into a single variable.
> 
> Even better: put them in the static, non-instanced, of the CssHtmlTree object.
> 
> CssHtmlTree.propertiesByGroup or something.
> 

Done

> @@ +567,5 @@
> > +        if (isNaN(parseInt(prop, 10))) {
> > +          break;
> > +        }
> > +        StyleGroupView.styleLookup[styles[prop]] = true;
> > +      }
> 
> It would be better to do:
> 
> for (let i = 0; i < styles.length; i++) {
>   let prop = styles.item(i);
>   // ...
> }
> 

That is what I did originally but for some reason in my build the style object had no length property, hence this hack. Newer versions have a length property again so ... Done.

> @@ +577,5 @@
> > +      let mergedArray;
> > +
> > +      if (!mergedArray) {
> > +        mergedArray = Array.concat(textArr, listArr, backgroundArr, dimsArr, posArr, borderArr, otherArr);
> > +      }
> 
> You have let mergedArray; which is followed by the check. The mergedArray array is always reconstructed, each time in the loop, for each cssProp. The result is never cached.
> 

Fixed

> @@ +590,5 @@
> > +    // If the newCSSProps Array is ever not empty at this point we
> > +    // need to move the properties into their appropriate category
> > +    newCSSProps.forEach(function(prop) {
> > +      otherArr.push(prop);
> > +    });
> 
> I would like the above code optimized for performance and brevity. Please merge the groups into one array only once, iterate only once through the ComputedCSSStyleDeclaration object returned by getComputedStyle(), and add the new props in the Other group, on-the-fly, in the same run.
> 

Done

> @@ +624,5 @@
> > +  this.localName = CssHtmlTree.l10n("style.group." + this.id);
> > +
> > +  this.propertyViews = [];
> > +  aPropertyNames.forEach(function(aPropertyName) {
> > +    if (this.isPropertySupported(aPropertyName)) {
> 
> Why do we need this check?
> 
> I believe this can be removed. All properties we have are supported. If we add props that are not supported, it's our error and we need to know that. Otherwise, properties that are added dynamically ... are obviously supported.
> 

CssHtmlTree.propertiesByGroup contains all current and as far, as I know, all CSS properties that may be added in the next couple of years. This is so that when the new properties are added they will already be in the correct category. That is why we have this check. Any properties that are missed will be added to the "other" category.

> @@ +701,5 @@
> > +   * @return {boolean} true or false
> > +   */
> > +  isPropertySupported: function(aProperty) {
> > +    return aProperty && StyleGroupView.styleLookup[aProperty];
> > +  },
> 
> See the comment above.
> 

See my comment above

> ::: browser/base/content/csshtmltree.xhtml
> @@ +62,5 @@
> > +        <span class="property-name">
> > +          <a class="link" target="_blank" title="&style.property.helpLinkTitle;"
> > +              href="${property.link}">${property.name}</a>
> > +        </span>
> > +        <span class="property-value">&quot;${property.value}&quot;</span>
> 
> I am not sure if we want quotes around the property value. We should have quotes only around those values that are of type string in CSS, but not for color keywords, for rgb(), rgba(), and so on. Definitely not for background, etc.
> 
> It would be simpler to just not add quotes at all. ;)
> 

Because of isPropertySupported the values are now never empty so I agree ... fixed.

> ::: toolkit/components/console/hudservice/HUDService.jsm
> @@ +4350,5 @@
> > +    let stylePanel = doc.getElementById("inspector-style-panel");
> > +    let cssLogic = new doc.defaultView.CssLogic();
> > +    let cssHtmlTree = new doc.defaultView.CssHtmlTree(styleWin, cssLogic);
> > +
> > +    if (stylePanel.hidden) {
> 
> This is not the correct way to check if a panel is open or not. Please see:
> 
> https://developer.mozilla.org/en/XUL/panel#p-state
> 

I know, it relic from the inspector. Changed.

> @@ +4358,5 @@
> > +      stylePanel.hidden = false;
> > +      // open at top right of browser panel, offset by 20px from top.
> > +      stylePanel.openPopup(browser, "end_before", 0, 20, false, false);
> > +      // size panel to 200px wide by half browser height - 60.
> > +      stylePanel.sizeTo(200, win.outerHeight / 2 - 60);
> 
> I am not sure if we really want this location. I found it awkward where the style panel opened up. Please also ask Robert about this. I think I'd prefer the style panel to open on top of the Web Console, or something, or even at the location of the node being inspected. The latter would probably be the best choice.

We are still debating how the panels are to be positioned and organized (we have a couple of tickets about it) so there is no point changing that as part of this bug.
Comment 142 Mihai Sucan [:msucan] 2011-05-19 13:48:22 PDT
Comment on attachment 533623 [details] [diff] [review]
CSSLogic + HtmlTree Upload 20

Review of attachment 533623 [details] [diff] [review]:
-----------------------------------------------------------------

Thank you for addressing the review comments!

With regards to the styling and RTL languages, please ask Ehsan to what degree does he want RTL support here.

Giving the patch r- because the tests fail and there are still some changes to be made. In the Web Console inspectstyle(document.body) does nothing now, and the Inspector fails as well.

::: browser/app/profile/firefox.js
@@ +992,5 @@
>  pref("devtools.errorconsole.enabled", false);
>  pref("devtools.inspector.enabled", false);
>  
> +// Disable the style inspector
> +pref("devtools.styleInspector.enabled", false);

I think I would like this lower case.

::: browser/base/content/csshtmltree.css
@@ +187,5 @@
> +.rule-specificty, .rule-status {
> +  white-space: nowrap;
> +}
> +.rule-link {
> +  text-align: right;

Not an expert in l10n and RTL languages, but I think you need to deal with dir=rtl. For RTL users you need to have the UI in a reversed order. Basically, whatever aligns on the right needs to align on the left, and vice-versa. Please see:

https://developer.mozilla.org/en/css_reference/mozilla_extensions

You have the things that allow you to do this, for example text-align: end.

::: browser/base/content/csshtmltree.js
@@ +568,5 @@
> +      for (let i = 0, numStyles = styles.length; i < numStyles; i++) {
> +        let prop = styles.item(i);
> +        CssHtmlTree.supportedPropertyLookup[prop] = true;
> +      }
> +    }

You don't need two ifs.

You can put this into the other if block. So you have:

if (!CssHtmlTree.propertiesByGroup) {
  ... propertiesByGroup ...
  ... supportedPropertyLookup ...
}

@@ +584,5 @@
> +        pbg.other
> +    );
> +    for (let cssProp in CssHtmlTree.supportedPropertyLookup) {
> +      if (CssHtmlTree.supportedPropertyLookup.hasOwnProperty(cssProp)) {
> +        if (mergedArray.indexOf(cssProp) < 0) {

Please use != -1.

@@ +585,5 @@
> +    );
> +    for (let cssProp in CssHtmlTree.supportedPropertyLookup) {
> +      if (CssHtmlTree.supportedPropertyLookup.hasOwnProperty(cssProp)) {
> +        if (mergedArray.indexOf(cssProp) < 0) {
> +          pbg.other.push(cssProp);

Why not do this in the main loop that builds supportedPropertyLookup?

for (let i = 0, numStyles = styles.length; i < numStyles; i++) {
  let prop = styles.item(i);
  CssHtmlTree.supportedPropertyLookup[prop] = true;
  if (mergedArray.indexOf(cssProp) != -1) {
    pbg.other.push(cssProp);
  }
}

@@ +588,5 @@
> +        if (mergedArray.indexOf(cssProp) < 0) {
> +          pbg.other.push(cssProp);
> +        }
> +      }
> +    }

You can put this whole thing into the main if block. You only need to do this once.

@@ +702,5 @@
> +   * 
> +   * @return {boolean} true or false
> +   */
> +  isPropertySupported: function(aProperty) {
> +    return aProperty && typeof CssHtmlTree.supportedPropertyLookup[aProperty] != "undefined";

Why not ...

return aProperty && aProperty in CssHtmlTree.supportedPropertyLookup;

?

@@ +764,5 @@
> +        this.showUnmatchedLinkClick(aEvent);
> +      } else {
> +        CssHtmlTree.template(this.templateRules, this.rules, this);
> +      }
> +      this.populated = true;

Can you please explain this code block? You made a change here that I am not sure I understand - long time since I last worked with the code.

::: browser/base/content/csslogic.js
@@ +616,5 @@
> +      display: CssLogic.getShortName(aElement),
> +      element: aElement
> +    });
> +    aElement = aElement.parentNode;
> +window.document.defaultView.Firebug.Console.log(aElement);

Please remove this line.

::: browser/base/content/styleinspector.js
@@ +17,5 @@
> + * The Original Code is the Mozilla Inspector Module.
> + *
> + * The Initial Developer of the Original Code is
> + * The Mozilla Foundation.
> + * Portions created by the Initial Developer are Copyright (C) 2010

2011

@@ +169,5 @@
> +
> +    if (!iframeWait) {
> +      aCallback();
> +    }
> +  },

The code of this method is somehow confusing.

I would expect that show() first adds the iframe, if needed. Then the callback is executed when the iframe page loads. Otherwise, you execute the callback. Simple as that. Perhaps the code logic is a bit confused, also the use of frameWait is confusing. Please organize the code flow better. You now have aCallback() in three places.

::: browser/base/content/templater.js
@@ +385,5 @@
> + * @param message the error message to report.
> + */
> +Templater.prototype.logError = function(message) {
> +  Services.console.logStringMessage(message);
> +};

The templater.js does not adhere to the coding guidelines. Arguments with the "a" prefix, and "let" instead of "var". The Templater prototype methods have no function names, etc. Also, why not:

Templater.prototype = {
  ...
};

Please ask Joe if he would accept such changes to the Templater code. It is his code.

::: toolkit/components/console/hudservice/HUDService.jsm
@@ +4409,5 @@
> +   */
> +  aJSTerm.sandbox.inspectstyle = function JSTH_inspectstyle(aNode)
> +  {
> +    aJSTerm.helperEvaluated = true;
> +    aJSTerm.openStylePanel(unwrap(aNode));

I don't think it's safe to unwrap the node. In openStylePanel you access properties that can be getters, that can execute code from the web page. You don't want that.

If I am wrong, please make an informed case for what you are doing.

@@ +4711,5 @@
> +   *        Object to display/inspect inside of the tree.
> +   */
> +  openStylePanel: function JST_openStylePanel(aNode)
> +  {
> +    if (!aNode || !aNode.nodeType || !aNode.style) {

Why not check the aNode instanceof? It should be an Ci.nsIDOMNode instance.

The aNode.style object should be an instanceof Ci.nsIDOMCSSStyleDeclaration.

@@ +4722,5 @@
> +    let styleInspector = doc.defaultView.styleInspector;
> +
> +    if (styleInspector) {
> +      styleInspector.selectNode(aNode);
> +    }

This is not exactly what I had in mind, but, hey, it is a better choice than what was in the previous patch iteration.

::: toolkit/locales/en-US/chrome/global/headsUpDisplay.properties
@@ +116,5 @@
>  ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
>  
> +# LOCALIZATION NOTE (inspectStyle.invalidObjectPassed):
> +# This message is returned when an invalid object is passed in to inspectstyle()
> +inspectStyle.invalidObjectPassed=The object is null or does not contain any styles

Missing a dot at the end.
Comment 143 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-30 03:22:17 PDT
Created attachment 536041 [details]
New Htmltree patch comparison in HTML format
Comment 144 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-30 03:29:01 PDT
Created attachment 536042 [details] [diff] [review]
CSSLogic + HtmlTree Upload 21

With regards to the styling and RTL languages, please ask Ehsan to what degree does he want RTL support here.
> 

I have made the appropriate changes to the stylesheet as requested.

> Giving the patch r- because the tests fail and there are still some changes to be made.
>

Tests now run fine

> In the Web Console inspectstyle(document.body) does nothing now, and the Inspector fails as well.
> 

inspectstyle(document.body) on any page works fine for me, it was preffed out by default and maybe you missed that. It is now preffed in by default.

> ::: browser/app/profile/firefox.js
> @@ +992,5 @@
> >  pref("devtools.errorconsole.enabled", false);
> >  pref("devtools.inspector.enabled", false);
> >  
> > +// Disable the style inspector
> > +pref("devtools.styleInspector.enabled", false);
> 
> I think I would like this lower case.
> 

Done ... now it is the only comment in the file that starts with a lower-case letter

> ::: browser/base/content/csshtmltree.css
> @@ +187,5 @@
> > +.rule-specificty, .rule-status {
> > +  white-space: nowrap;
> > +}
> > +.rule-link {
> > +  text-align: right;
> 
> Not an expert in l10n and RTL languages, but I think you need to deal with dir=rtl. For RTL users you need to have the UI in a reversed order. Basically, whatever aligns on the right needs to align on the left, and vice-versa. Please see:
> 
> https://developer.mozilla.org/en/css_reference/mozilla_extensions
> 
> You have the things that allow you to do this, for example text-align: end.
> 

Fixed

> ::: browser/base/content/csshtmltree.js
> @@ +568,5 @@
> > +      for (let i = 0, numStyles = styles.length; i < numStyles; i++) {
> > +        let prop = styles.item(i);
> > +        CssHtmlTree.supportedPropertyLookup[prop] = true;
> > +      }
> > +    }
> 
> You don't need two ifs.
> 
> You can put this into the other if block. So you have:
> 
> if (!CssHtmlTree.propertiesByGroup) {
>   ... propertiesByGroup ...
>   ... supportedPropertyLookup ...
> }
> 

Done

> @@ +584,5 @@
> > +        pbg.other
> > +    );
> > +    for (let cssProp in CssHtmlTree.supportedPropertyLookup) {
> > +      if (CssHtmlTree.supportedPropertyLookup.hasOwnProperty(cssProp)) {
> > +        if (mergedArray.indexOf(cssProp) < 0) {
> 
> Please use != -1.
> 

Done

> @@ +585,5 @@
> > +    );
> > +    for (let cssProp in CssHtmlTree.supportedPropertyLookup) {
> > +      if (CssHtmlTree.supportedPropertyLookup.hasOwnProperty(cssProp)) {
> > +        if (mergedArray.indexOf(cssProp) < 0) {
> > +          pbg.other.push(cssProp);
> 
> Why not do this in the main loop that builds supportedPropertyLookup?
> 
> for (let i = 0, numStyles = styles.length; i < numStyles; i++) {
>   let prop = styles.item(i);
>   CssHtmlTree.supportedPropertyLookup[prop] = true;
>   if (mergedArray.indexOf(cssProp) != -1) {
>     pbg.other.push(cssProp);
>   }
> }
> 

Done

> @@ +588,5 @@
> > +        if (mergedArray.indexOf(cssProp) < 0) {
> > +          pbg.other.push(cssProp);
> > +        }
> > +      }
> > +    }
> 
> You can put this whole thing into the main if block. You only need to do this once.
> 

Still the same point I guess ... done.

> @@ +702,5 @@
> > +   * 
> > +   * @return {boolean} true or false
> > +   */
> > +  isPropertySupported: function(aProperty) {
> > +    return aProperty && typeof CssHtmlTree.supportedPropertyLookup[aProperty] != "undefined";
> 
> Why not ...
> 
> return aProperty && aProperty in CssHtmlTree.supportedPropertyLookup;
> 
> ?
> 

Done

> @@ +764,5 @@
> > +        this.showUnmatchedLinkClick(aEvent);
> > +      } else {
> > +        CssHtmlTree.template(this.templateRules, this.rules, this);
> > +      }
> > +      this.populated = true;
> 
> Can you please explain this code block? You made a change here that I am not sure I understand - long time since I last worked with the code.
> 

Previous to this change and it's accompanying template code, when a css property had 0 rules but also had >0 unmatched rules there was no arrow to show that the property could be expanded ... the only way to find them was to click every properties row and see if anything appeared. We now show "X unmatched rules" to the right of the CSS property to make this clear. Clicking on this link to show the unmatched rules directly is what this code is all about.

> ::: browser/base/content/csslogic.js
> @@ +616,5 @@
> > +      display: CssLogic.getShortName(aElement),
> > +      element: aElement
> > +    });
> > +    aElement = aElement.parentNode;
> > +window.document.defaultView.Firebug.Console.log(aElement);
> 
> Please remove this line.
>

Done
 
> ::: browser/base/content/styleinspector.js
> @@ +17,5 @@
> > + * The Original Code is the Mozilla Inspector Module.
> > + *
> > + * The Initial Developer of the Original Code is
> > + * The Mozilla Foundation.
> > + * Portions created by the Initial Developer are Copyright (C) 2010
> 
> 2011
> 

Wow, time flies when you are having fun ;o)

> @@ +169,5 @@
> > +
> > +    if (!iframeWait) {
> > +      aCallback();
> > +    }
> > +  },
> 
> The code of this method is somehow confusing.
> 
> I would expect that show() first adds the iframe, if needed. Then the callback is executed when the iframe page loads. Otherwise, you execute the callback. Simple as that. Perhaps the code logic is a bit confused, also the use of frameWait is confusing. Please organize the code flow better. You now have aCallback() in three places.
> 

Fixed

> ::: browser/base/content/templater.js
> @@ +385,5 @@
> > + * @param message the error message to report.
> > + */
> > +Templater.prototype.logError = function(message) {
> > +  Services.console.logStringMessage(message);
> > +};
> 
> The templater.js does not adhere to the coding guidelines. Arguments with the "a" prefix, and "let" instead of "var". The Templater prototype methods have no function names, etc. Also, why not:
> 
> Templater.prototype = {
>   ...
> };
> 
> Please ask Joe if he would accept such changes to the Templater code. It is his code.
> 

Joe says to leave it as it is.

> ::: toolkit/components/console/hudservice/HUDService.jsm
> @@ +4409,5 @@
> > +   */
> > +  aJSTerm.sandbox.inspectstyle = function JSTH_inspectstyle(aNode)
> > +  {
> > +    aJSTerm.helperEvaluated = true;
> > +    aJSTerm.openStylePanel(unwrap(aNode));
> 
> I don't think it's safe to unwrap the node. In openStylePanel you access properties that can be getters, that can execute code from the web page. You don't want that.
> 
> If I am wrong, please make an informed case for what you are doing.
> 

Done.

> @@ +4711,5 @@
> > +   *        Object to display/inspect inside of the tree.
> > +   */
> > +  openStylePanel: function JST_openStylePanel(aNode)
> > +  {
> > +    if (!aNode || !aNode.nodeType || !aNode.style) {
> 
> Why not check the aNode instanceof? It should be an Ci.nsIDOMNode instance.
> 
> The aNode.style object should be an instanceof Ci.nsIDOMCSSStyleDeclaration.
> 

Done.

> @@ +4722,5 @@
> > +    let styleInspector = doc.defaultView.styleInspector;
> > +
> > +    if (styleInspector) {
> > +      styleInspector.selectNode(aNode);
> > +    }
> 
> This is not exactly what I had in mind, but, hey, it is a better choice than what was in the previous patch iteration.
> 

Well, that code was from another bug but I figured that we may as well get it reviewed by merging with this big patch.

> ::: toolkit/locales/en-US/chrome/global/headsUpDisplay.properties
> @@ +116,5 @@
> >  ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
> >  
> > +# LOCALIZATION NOTE (inspectStyle.invalidObjectPassed):
> > +# This message is returned when an invalid object is passed in to inspectstyle()
> > +inspectStyle.invalidObjectPassed=The object is null or does not contain any styles
> 
> Missing a dot at the end.

Done
Comment 145 Michael Ratcliffe [:miker] [:mratcliffe] 2011-05-31 09:22:29 PDT
Created attachment 536321 [details] [diff] [review]
CSSLogic + HtmlTree Upload 22

Highlighter needs to be installed first
Comment 146 Mihai Sucan [:msucan] 2011-05-31 12:30:01 PDT
(In reply to comment #144)
> > In the Web Console inspectstyle(document.body) does nothing now, and the Inspector fails as well.
> > 
> 
> inspectstyle(document.body) on any page works fine for me, it was preffed
> out by default and maybe you missed that. It is now preffed in by default.

Yeah, new features need to be preffed on by default, unless we consider they are not ready for public usage.

> > ::: browser/app/profile/firefox.js
> > @@ +992,5 @@
> > >  pref("devtools.errorconsole.enabled", false);
> > >  pref("devtools.inspector.enabled", false);
> > >  
> > > +// Disable the style inspector
> > > +pref("devtools.styleInspector.enabled", false);
> > 
> > I think I would like this lower case.
> > 
> 
> Done ... now it is the only comment in the file that starts with a
> lower-case letter

I meant the pref name, not the comment. ;)

> > @@ +764,5 @@
> > > +        this.showUnmatchedLinkClick(aEvent);
> > > +      } else {
> > > +        CssHtmlTree.template(this.templateRules, this.rules, this);
> > > +      }
> > > +      this.populated = true;
> > 
> > Can you please explain this code block? You made a change here that I am not sure I understand - long time since I last worked with the code.
> > 
> 
> Previous to this change and it's accompanying template code, when a css
> property had 0 rules but also had >0 unmatched rules there was no arrow to
> show that the property could be expanded ... the only way to find them was
> to click every properties row and see if anything appeared. We now show "X
> unmatched rules" to the right of the CSS property to make this clear.
> Clicking on this link to show the unmatched rules directly is what this code
> is all about.

Thank you for your explanation. Please add a comment in the function succinctly explaining this.

(incoming review comments as well)
Comment 147 Mihai Sucan [:msucan] 2011-06-01 06:17:15 PDT
Comment on attachment 536321 [details] [diff] [review]
CSSLogic + HtmlTree Upload 22

Review of attachment 536321 [details] [diff] [review]:
-----------------------------------------------------------------

Thanks for the improvements in the patch.

Here is another batch of comments:

- Regarding RTL support, you can test it by adding intl.uidirection.en = rtl to about:config. With this change your style panel content doesn't change the direction. I am not sure how you should approach matters here, but please ask for review from Ehsan, for RTL. Again, please ping him.

- When I call inspectstyle(element1) and inspectstyle(element2) I would expect to have two style panels open, so I can compare the two element styles. The code currently assumes there can only be one Style Panel in the browser window. Please make it such that we can create multiple instances of the StyleInspector object.

Consumers like the Web Console create multiple instances of the StyleInspector for each node. The Inspector tool will create only one instance of the StyleInspector panel which will be updated based on the selected/highlighted element.

- If I have two Web Consoles open, in two tabs, in the same window, calling inspectstyle() in each of these Web Consoles overwrites the previous panel. Again, the user would expect a second Style panel to open up.

- When I close the Web Console, the associated Style panel does not close.

- Why is there only one test for the style inspector? We had several more tests.

- Please add a test to the HUDService for the new inspectstyle() function.

- The patch has trailing white-spaces in multiple places. Please clean it up.

Comments specific to styleinspector.js:

- I think you do not need to call selectNode(null) in hide(). The user may just want to temporarily hide the style panel, not really change what's displayed inside it.

- In show() you have two mechanisms of letting the consumer be notified about the style panel opening: a callback function and the styleinspector-ready notification. Please use only the latter.

Additionally, you should notify the observers about styleinspector-ready only after the style panel sends the popupshown event.

- I still find it confusing the way you have _init() and show().

- I don't think you need to check if isEnabled in each of the styleinspector methods. That should be the problem of the consumer. The Web Console or the Inspector tool should not provide the means of using the style panel in the first place (when the style panel is not enabled).

That's all for now. Looking forward to the updated patch. Thanks!

::: browser/app/profile/firefox.js
@@ +1008,5 @@
>  // Enable the Inspector
>  pref("devtools.inspector.enabled", true);
>  
> +// enable the style inspector
> +pref("devtools.styleInspector.enabled", true);

I actually meant the pref name to be lower case, not the comment.

::: browser/base/content/csshtmltree.css
@@ +10,5 @@
> + * 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 mozilla.org code.

the Mozilla Inspector Module.

@@ +13,5 @@
> + *
> + * The Original Code is mozilla.org code.
> + *
> + * The Initial Developer of the Original Code is
> + * Netscape Communications Corporation.

The Mozilla Foundation

@@ +14,5 @@
> + * The Original Code is mozilla.org code.
> + *
> + * The Initial Developer of the Original Code is
> + * Netscape Communications Corporation.
> + * Portions created by the Initial Developer are Copyright (C) 1998-1999

2011

@@ +212,5 @@
> +}
> +.rule-link {
> +  text-align: end;
> +}
> +/* statuses - we'll clear these names up */

Please make the status names clearer.

::: browser/base/content/csshtmltree.js
@@ +155,5 @@
> +        let styleInspector = window.styleInspector;
> +
> +        if (styleInspector) {
> +          styleInspector.selectNode(node);
> +        }

You can replace the else block with:

window.styleInspector.selectNode(aEvent.target.pathElement);

::: browser/base/content/csshtmltree.xhtml
@@ +11,5 @@
> +  <meta http-equiv="Content-Type"
> +    content="application/xhtml+xml; charset=UTF-8" />
> +  <link rel="stylesheet" type="text/css"
> +    href="chrome://browser/content/csshtmltree.css" />
> +</head>

Please add the MPL tri license header to this file as well.

::: browser/base/content/styleinspector.js
@@ +96,5 @@
> +   */
> +  _init: function(aCallback)
> +  {
> +    let doc = window.document;
> +    let styleWin = doc.getElementById("inspector-style-iframe");

You can use document directly, throughout the function.

@@ +99,5 @@
> +    let doc = window.document;
> +    let styleWin = doc.getElementById("inspector-style-iframe");
> +
> +    this.stylePanel = this.stylePanel || doc.getElementById("inspector-style-panel");
> +    this.cssLogic = new doc.defaultView.CssLogic();

doc.defaultView is window, so you can directly do new CssLogic().

@@ +110,5 @@
> +      styleWin.setAttribute("tooltip", "aHTMLTooltip");
> +      let panel = doc.getElementById("inspector-style-panel");
> +      let hbox = doc.getElementById("inspector-style-panel-hbox");
> +      panel.insertBefore(styleWin, hbox);
> +      let _this = this;

let self = this;

Generally we use self for such purposes.

@@ +117,5 @@
> +      // onload event of iframes containing xhtml documents is raised by
> +      // iframe.contentDocument and not the iframe itself
> +      styleWin.addEventListener("load", function SI_iframe_onload() {
> +        styleWin.removeEventListener("load", arguments.callee, false);
> +        _this.cssHtmlTree = new doc.defaultView.CssHtmlTree(styleWin, _this.cssLogic);

new CssHtmlTree()

@@ +122,5 @@
> +        if (aCallback) {
> +          aCallback();
> +        }
> +        Services.obs.notifyObservers(null, "styleinspector-ready", null);
> +      }, true);

The third argument for addEventListener() is true, but when you call removeEventListener() you use false. Please decide which one you want to use.

@@ +138,5 @@
> +  {
> +    if (this.isEnabled) {
> +      if (!this.isOpen) {
> +        let doc = window.document;
> +        let stylePanel = this.stylePanel || doc.getElementById("inspector-style-panel");

Please use document directly.

@@ +139,5 @@
> +    if (this.isEnabled) {
> +      if (!this.isOpen) {
> +        let doc = window.document;
> +        let stylePanel = this.stylePanel || doc.getElementById("inspector-style-panel");
> +        let browser = doc.defaultView.gBrowser.selectedBrowser;

doc.defaultView is window. You can directly access gBrowser.selectedBrowser.

@@ +152,5 @@
> +      if (this.isInitialized) {
> +        if (aCallback) {
> +          aCallback();
> +        }
> +        Services.obs.notifyObservers(null, "styleinspector-ready", null);

Please use styleinspector-opened.

@@ +167,5 @@
> +  {
> +    if (this.isEnabled && this.isOpen) {
> +      this.stylePanel.hidePopup();
> +      this.selectNode(null);
> +      Services.obs.notifyObservers(null, "styleinspector-closed", null);

Please wait for the stylePanel popuphidden event before notifying the observers that the styleinspector-closed.

@@ +187,5 @@
> +        let _this = this;
> +        let callback = function SI_toggle_Callback() {
> +          _this.selectNode(aNode);
> +        };
> +        this.show(callback);

this.show(this.selectNode.bind(this, aNode)); achieves the same.

::: browser/base/content/test/Makefile.in
@@ +42,5 @@
>  
>  DIRS += \
>  		tabview \
>  		inspector \
> +		styleinspector \

Please ask Robert if we really need a styleinspector folder for tests. I believe we do not need it, yet.

::: browser/base/content/test/styleinspector/browser_styleinspector.js
@@ +57,5 @@
> +    '<p id="closing">end transmission</p>\n' +
> +    '<p>Inspect using inspectstyle(document.querySelectorAll("span")[0])</p>' +
> +    '</div>';
> +  doc.title = "Style Inspector Test";
> +  ok(styleInspector, "styleInspector exists");

window.styleInspector

@@ +66,5 @@
> +function runStyleInspectorTests()
> +{
> +  Services.obs.removeObserver(runStyleInspectorTests, "styleinspector-ready", false);
> +
> +  ok(styleInspector.isEnabled, "style inspector preference is enabled");

Move this check before you try to show the panel.

@@ +127,5 @@
> +
> +function finishUp()
> +{
> +  styleInspector.hide();
> +  ok(!styleInspector.isOpen, "style inspector is closed");

You should wait for the styleinspector-closed notification before checking. We need to assume that hide() is async.

::: browser/locales/en-US/chrome/browser/styleinspector.properties
@@ +1,4 @@
> +# LOCALIZATION NOTE (object.objectPanelTitle): used in the Object Panel in the
> +# Inspector tool. There's also inspectObjectButton in browser.dtd for the
> +# toolbar button which allows users to open/close the Object panel.
> +object.objectPanelTitle=Object

This string is not used anywhere in the patch.

@@ +23,5 @@
> +# the Style panel of the Inspector tool. For each style property the developer
> +# can mark it as important, or not. This string is displayed in the hover tool
> +# tip when the user is on top of a rule within a property view, if the CSS
> +# property is marked as important in that rule.
> +style.property.important=!important,

This one is also unused.

@@ +57,5 @@
> +# LOCALIZATION NOTE (style.elementSelector): This is used inside the Style panel
> +# of the Inspector tool. For each property the panel shows the rule with its
> +# selector. Rules can come from element.style. In this case, one can translate
> +# element.style to the local language.
> +style.elementSelector=element.style

This one is also unused. Why?

::: toolkit/components/console/hudservice/HUDService.jsm
@@ +4713,5 @@
> +   *        Object to display/inspect inside of the tree.
> +   */
> +  openStylePanel: function JST_openStylePanel(aNode)
> +  {
> +    let Ci = Components.interfaces;

Ci is already defined in HUDService.jsm.

@@ +4716,5 @@
> +  {
> +    let Ci = Components.interfaces;
> +
> +    if (!aNode || !(aNode instanceof Ci.nsIDOMNode ||
> +        aNode instanceof Ci.nsIDOMCSSStyleDeclaration)) {

You allow aNode to be instanceof nsIDOMCSSStyleDeclaration, which is not correct.

In my previous review comment I asked for:

if (!aNode || !(aNode instanceof Ci.nsIDOMNode) || !(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration))

(it should work, if it doesn't, please explain why it doesn't)

@@ +4718,5 @@
> +
> +    if (!aNode || !(aNode instanceof Ci.nsIDOMNode ||
> +        aNode instanceof Ci.nsIDOMCSSStyleDeclaration)) {
> +      let errstr = HUDService.getStr("inspectStyle.invalidObjectPassed");
> +      this.writeOutput(errstr + "\n", CATEGORY_OUTPUT, SEVERITY_LOG);

Shouldn't it be SEVERITY_ERROR?

@@ +4725,5 @@
> +
> +    let doc = this.parentNode.ownerDocument;
> +    let styleInspector = doc.defaultView.styleInspector;
> +
> +    if (styleInspector) {

This is a redundant check. You don't need to do this. We expect the code runs correctly. If it doesn't, we want it to throw.
Comment 148 Axel Hecht [:Pike] 2011-06-01 07:22:35 PDT
Comment on attachment 536321 [details] [diff] [review]
CSSLogic + HtmlTree Upload 22

Review of attachment 536321 [details] [diff] [review]:
-----------------------------------------------------------------

I'd really like to see a current screenshot to have a better idea what we're talking about. Not sure how close the obsolete one is to what's currently there. Also, what Mihai said.

::: browser/locales/en-US/chrome/browser/browser.dtd
@@ +211,5 @@
>  
>  <!ENTITY inspectPanelTitle.label      "HTML">
>  <!ENTITY inspectButton.label          "Inspect">
>  <!ENTITY inspectButton.accesskey      "I">
> +<!ENTITY inspectStylePanelTitle.label "Style">

This doesn't have an accesskey?

::: browser/locales/en-US/chrome/browser/styleinspector.dtd
@@ +1,3 @@
> +<!-- LOCALIZATION NOTE (style.panelTitle): This is used inside the Style panel
> +  -  of the Inspector tool. This is the panel title string used inside the web
> +  -  page of the panel. -->

It's good to have localization notes, but they're very repetitive here. The least amount of folks are going to translate each of these strings weeks apart.

Also, there is the filename that indicates where we are already. Make these notes short and to the point?

Style is used for Cascading Style Sheets (CSS) rules.
Comment 149 :Ehsan Akhgari (busy, don't ask for review please) 2011-06-03 14:17:25 PDT
(In reply to comment #147)
> - Regarding RTL support, you can test it by adding intl.uidirection.en = rtl to
> about:config. With this change your style panel content doesn't change the
> direction. I am not sure how you should approach matters here, but please ask
> for review from Ehsan, for RTL. Again, please ping him.

It would also be helpful for me to have a tryserver build which I can use for the RTL review, when you need it.
Comment 150 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-07 07:02:24 PDT
The try build will be available at the following URL as soon as it finishes building:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mratcliffe@mozilla.com-0d66b89236c4
Comment 151 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-07 07:03:37 PDT
Created attachment 537780 [details]
Style inspector screenshot
Comment 152 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-07 07:59:43 PDT
Created attachment 537793 [details] [diff] [review]
CSSLogic + HtmlTree Upload 22

> > > In the Web Console inspectstyle(document.body) does nothing now, and the Inspector fails as well.
> > > 
> > 
> > inspectstyle(document.body) on any page works fine for me, it was preffed
> > out by default and maybe you missed that. It is now preffed in by default.
> 
> Yeah, new features need to be preffed on by default, unless we consider they are not ready for public usage.
> 
> > > @@ +764,5 @@
> > > > +        this.showUnmatchedLinkClick(aEvent);
> > > > +      } else {
> > > > +        CssHtmlTree.template(this.templateRules, this.rules, this);
> > > > +      }
> > > > +      this.populated = true;
> > > 
> > > Can you please explain this code block? You made a change here that I am not sure I understand - long time since I last worked with the code.
> > > 
> > 
> > Previous to this change and it's accompanying template code, when a css
> > property had 0 rules but also had >0 unmatched rules there was no arrow to
> > show that the property could be expanded ... the only way to find them was
> > to click every properties row and see if anything appeared. We now show "X
> > unmatched rules" to the right of the CSS property to make this clear.
> > Clicking on this link to show the unmatched rules directly is what this code
> > is all about.
> 
> Thank you for your explanation. Please add a comment in the function succinctly explaining this.
> 

Done

> - Regarding RTL support, you can test it by adding intl.uidirection.en = rtl to about:config. With this change your style panel content doesn't change the direction. I am not sure how you should approach matters here, but please ask for review from Ehsan, for RTL. Again, please ping him.
> 

Because the iframe is content (even when type="chrome") it does not inherit from the UI and it is not possible to use -moz-locale-dir in non-chrome stuff. I have added a bunch of workarounds to make it work ... fixed.

> - When I call inspectstyle(element1) and inspectstyle(element2) I would expect to have two style panels open, so I can compare the two element styles. The code currently assumes there can only be one Style Panel in the browser window. Please make it such that we can create multiple instances of the StyleInspector object.
> 
> Consumers like the Web Console create multiple instances of the StyleInspector for each node. The Inspector tool will create only one instance of the StyleInspector panel which will be updated based on the selected/highlighted element.
> 
> - If I have two Web Consoles open, in two tabs, in the same window, calling inspectstyle() in each of these Web Consoles overwrites the previous panel. Again, the user would expect a second Style panel to open up.
> 

Agreed, disagreed, agreed, disagreed, agreed ... done

> - When I close the Web Console, the associated Style panel does not close.
> 

Fixed

> - Why is there only one test for the style inspector? We had several more tests.
> 

Those tests were for Inspector & styleInspector integration. Actually, there was nothing covered in those tests that is not covered in the single test, there was a lot of repetition. in fact, the single test has far better code coverage. I do plan on adding another test when the Inspector & styleInspector can communicate with one another.

> - Please add a test to the HUDService for the new inspectstyle() function.
> 

Done

> - The patch has trailing white-spaces in multiple places. Please clean it up.
> 

Seems to be a general problem throughout the Firefox source. I have now removed all trailing whitespace from all of my changes.

> Comments specific to styleinspector.js:
> 
> - I think you do not need to call selectNode(null) in hide(). The user may just want to temporarily hide the style panel, not really change what's displayed inside it.
> 

Done

> - In show() you have two mechanisms of letting the consumer be notified about the style panel opening: a callback function and the styleinspector-ready notification. Please use only the latter.
> 
> Additionally, you should notify the observers about styleinspector-ready only after the style panel sends the popupshown event.
> 
> - I still find it confusing the way you have _init() and show().
> 
> - I don't think you need to check if isEnabled in each of the styleinspector methods. That should be the problem of the consumer. The Web Console or the Inspector tool should not provide the means of using the style panel in the first place (when the style panel is not enabled).
> 

Done

> 
> ::: browser/app/profile/firefox.js
> @@ +1008,5 @@
> >  // Enable the Inspector
> >  pref("devtools.inspector.enabled", true);
> >  
> > +// enable the style inspector
> > +pref("devtools.styleInspector.enabled", true);
> 
> I actually meant the pref name to be lower case, not the comment.
> 

Now that makes much more sense ... done.

> ::: browser/base/content/csshtmltree.css
> @@ +10,5 @@
> > + * 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 mozilla.org code.
> 
> the Mozilla Inspector Module.
> 

Changed

> @@ +13,5 @@
> > + *
> > + * The Original Code is mozilla.org code.
> > + *
> > + * The Initial Developer of the Original Code is
> > + * Netscape Communications Corporation.
> 
> The Mozilla Foundation
> 

Changed

> @@ +14,5 @@
> > + * The Original Code is mozilla.org code.
> > + *
> > + * The Initial Developer of the Original Code is
> > + * Netscape Communications Corporation.
> > + * Portions created by the Initial Developer are Copyright (C) 1998-1999
> 
> 2011
> 
> @@ +212,5 @@
> > +}
> > +.rule-link {
> > +  text-align: end;
> > +}
> > +/* statuses - we'll clear these names up */
> 
> Please make the status names clearer.
> 

Done

> ::: browser/base/content/csshtmltree.js
> @@ +155,5 @@
> > +        let styleInspector = window.styleInspector;
> > +
> > +        if (styleInspector) {
> > +          styleInspector.selectNode(node);
> > +        }
> 
> You can replace the else block with:
> 
> window.styleInspector.selectNode(aEvent.target.pathElement);
> 

Done

> ::: browser/base/content/csshtmltree.xhtml
> @@ +11,5 @@
> > +  <meta http-equiv="Content-Type"
> > +    content="application/xhtml+xml; charset=UTF-8" />
> > +  <link rel="stylesheet" type="text/css"
> > +    href="chrome://browser/content/csshtmltree.css" />
> > +</head>
> 
> Please add the MPL tri license header to this file as well.
> 

Done

> ::: browser/base/content/styleinspector.js
> @@ +96,5 @@
> > +   */
> > +  _init: function(aCallback)
> > +  {
> > +    let doc = window.document;
> > +    let styleWin = doc.getElementById("inspector-style-iframe");
> 
> You can use document directly, throughout the function.
> 

Done

> @@ +99,5 @@
> > +    let doc = window.document;
> > +    let styleWin = doc.getElementById("inspector-style-iframe");
> > +
> > +    this.stylePanel = this.stylePanel || doc.getElementById("inspector-style-panel");
> > +    this.cssLogic = new doc.defaultView.CssLogic();
> 
> doc.defaultView is window, so you can directly do new CssLogic().
> 

Done

> @@ +110,5 @@
> > +      styleWin.setAttribute("tooltip", "aHTMLTooltip");
> > +      let panel = doc.getElementById("inspector-style-panel");
> > +      let hbox = doc.getElementById("inspector-style-panel-hbox");
> > +      panel.insertBefore(styleWin, hbox);
> > +      let _this = this;
> 
> let self = this;
> 
> Generally we use self for such purposes.
> 

Done

> @@ +117,5 @@
> > +      // onload event of iframes containing xhtml documents is raised by
> > +      // iframe.contentDocument and not the iframe itself
> > +      styleWin.addEventListener("load", function SI_iframe_onload() {
> > +        styleWin.removeEventListener("load", arguments.callee, false);
> > +        _this.cssHtmlTree = new doc.defaultView.CssHtmlTree(styleWin, _this.cssLogic);
> 
> new CssHtmlTree()
> 
> @@ +122,5 @@
> > +        if (aCallback) {
> > +          aCallback();
> > +        }
> > +        Services.obs.notifyObservers(null, "styleinspector-ready", null);
> > +      }, true);
> 
> The third argument for addEventListener() is true, but when you call removeEventListener() you use false. Please decide which one you want to use.
> 

Hmmm ... it shouldn't have worked so I didn't notice that. Use of useCapture is now optional in removeEventListener from Firefox 6 and onwards so I have removed it instead.

> @@ +138,5 @@
> > +  {
> > +    if (this.isEnabled) {
> > +      if (!this.isOpen) {
> > +        let doc = window.document;
> > +        let stylePanel = this.stylePanel || doc.getElementById("inspector-style-panel");
> 
> Please use document directly.
> 

Done

> @@ +139,5 @@
> > +    if (this.isEnabled) {
> > +      if (!this.isOpen) {
> > +        let doc = window.document;
> > +        let stylePanel = this.stylePanel || doc.getElementById("inspector-style-panel");
> > +        let browser = doc.defaultView.gBrowser.selectedBrowser;
> 
> doc.defaultView is window. You can directly access gBrowser.selectedBrowser.
> 

Of course I can ... fixed.

> @@ +152,5 @@
> > +      if (this.isInitialized) {
> > +        if (aCallback) {
> > +          aCallback();
> > +        }
> > +        Services.obs.notifyObservers(null, "styleinspector-ready", null);
> 
> Please use styleinspector-opened.
> 

Done

> @@ +167,5 @@
> > +  {
> > +    if (this.isEnabled && this.isOpen) {
> > +      this.stylePanel.hidePopup();
> > +      this.selectNode(null);
> > +      Services.obs.notifyObservers(null, "styleinspector-closed", null);
> 
> Please wait for the stylePanel popuphidden event before notifying the observers that the styleinspector-closed.
> 

Done

> @@ +187,5 @@
> > +        let _this = this;
> > +        let callback = function SI_toggle_Callback() {
> > +          _this.selectNode(aNode);
> > +        };
> > +        this.show(callback);
> 
> this.show(this.selectNode.bind(this, aNode)); achieves the same.
> 
> ::: browser/base/content/test/Makefile.in
> @@ +42,5 @@
> >  
> >  DIRS += \
> >     tabview \
> >     inspector \
> > +   styleinspector \
> 
> Please ask Robert if we really need a styleinspector folder for tests. I believe we do not need it, yet.
> 

True ... done

> ::: browser/base/content/test/styleinspector/browser_styleinspector.js
> @@ +57,5 @@
> > +    '<p id="closing">end transmission</p>\n' +
> > +    '<p>Inspect using inspectstyle(document.querySelectorAll("span")[0])</p>' +
> > +    '</div>';
> > +  doc.title = "Style Inspector Test";
> > +  ok(styleInspector, "styleInspector exists");
> 
> window.styleInspector
> 

Done

> @@ +66,5 @@
> > +function runStyleInspectorTests()
> > +{
> > +  Services.obs.removeObserver(runStyleInspectorTests, "styleinspector-ready", false);
> > +
> > +  ok(styleInspector.isEnabled, "style inspector preference is enabled");
> 
> Move this check before you try to show the panel.
> 

Done

> @@ +127,5 @@
> > +
> > +function finishUp()
> > +{
> > +  styleInspector.hide();
> > +  ok(!styleInspector.isOpen, "style inspector is closed");
> 
> You should wait for the styleinspector-closed notification before checking. We need to assume that hide() is async.
> 

Done

> ::: browser/locales/en-US/chrome/browser/styleinspector.properties
> @@ +1,4 @@
> > +# LOCALIZATION NOTE (object.objectPanelTitle): used in the Object Panel in the
> > +# Inspector tool. There's also inspectObjectButton in browser.dtd for the
> > +# toolbar button which allows users to open/close the Object panel.
> > +object.objectPanelTitle=Object
> 
> This string is not used anywhere in the patch.
> 

Removed

> @@ +23,5 @@
> > +# the Style panel of the Inspector tool. For each style property the developer
> > +# can mark it as important, or not. This string is displayed in the hover tool
> > +# tip when the user is on top of a rule within a property view, if the CSS
> > +# property is marked as important in that rule.
> > +style.property.important=!important,
> 
> This one is also unused.
> 

Removed

> @@ +57,5 @@
> > +# LOCALIZATION NOTE (style.elementSelector): This is used inside the Style panel
> > +# of the Inspector tool. For each property the panel shows the rule with its
> > +# selector. Rules can come from element.style. In this case, one can translate
> > +# element.style to the local language.
> > +style.elementSelector=element.style
> 
> This one is also unused. Why?
> 

Because we don't need it anywhere ... removed

> ::: toolkit/components/console/hudservice/HUDService.jsm
> @@ +4713,5 @@
> > +   *        Object to display/inspect inside of the tree.
> > +   */
> > +  openStylePanel: function JST_openStylePanel(aNode)
> > +  {
> > +    let Ci = Components.interfaces;
> 
> Ci is already defined in HUDService.jsm.
> 

Removed

> @@ +4716,5 @@
> > +  {
> > +    let Ci = Components.interfaces;
> > +
> > +    if (!aNode || !(aNode instanceof Ci.nsIDOMNode ||
> > +        aNode instanceof Ci.nsIDOMCSSStyleDeclaration)) {
> 
> You allow aNode to be instanceof nsIDOMCSSStyleDeclaration, which is not correct.
> 
> In my previous review comment I asked for:
> 
> if (!aNode || !(aNode instanceof Ci.nsIDOMNode) || !(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration))
> 
> (it should work, if it doesn't, please explain why it doesn't)
> 

Done

> @@ +4718,5 @@
> > +
> > +    if (!aNode || !(aNode instanceof Ci.nsIDOMNode ||
> > +        aNode instanceof Ci.nsIDOMCSSStyleDeclaration)) {
> > +      let errstr = HUDService.getStr("inspectStyle.invalidObjectPassed");
> > +      this.writeOutput(errstr + "\n", CATEGORY_OUTPUT, SEVERITY_LOG);
> 
> Shouldn't it be SEVERITY_ERROR?
> 

Yes, it should ... done

> @@ +4725,5 @@
> > +
> > +    let doc = this.parentNode.ownerDocument;
> > +    let styleInspector = doc.defaultView.styleInspector;
> > +
> > +    if (styleInspector) {
> 
> This is a redundant check. You don't need to do this. We expect the code runs correctly. If it doesn't, we want it to throw.
> 

Good, fixed.

> I'd really like to see a current screenshot to have a better idea what we're talking about. Not sure how close the obsolete one is to what's currently there. Also, what Mihai said.
> 
An image is available at:
https://bug582596.bugzilla.mozilla.org/attachment.cgi?id=537780

I am not sure what happened to the colors in the screenshot but you can read the styles etc. just fine.

You would probably be far better off with a try build. You can get one from:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mratcliffe@mozilla.com-0d66b89236c4

To open the style inspector:
- <ctrl><shift><k> to open the web console
- Type inspectstyle(document.body) in the web console

> ::: browser/locales/en-US/chrome/browser/browser.dtd
> @@ +211,5 @@
> >  
> >  <!ENTITY inspectPanelTitle.label      "HTML">
> >  <!ENTITY inspectButton.label          "Inspect">
> >  <!ENTITY inspectButton.accesskey      "I">
> > +<!ENTITY inspectStylePanelTitle.label "Style">
> 
> This doesn't have an accesskey?
> 

No, it is only launched using e.g. inspectstyle(document.body) from the web console.

> ::: browser/locales/en-US/chrome/browser/styleinspector.dtd
> @@ +1,3 @@
> > +<!-- LOCALIZATION NOTE (style.panelTitle): This is used inside the Style panel
> > +  -  of the Inspector tool. This is the panel title string used inside the web
> > +  -  page of the panel. -->
> 
> It's good to have localization notes, but they're very repetitive here. The least amount of folks are going to translate each of these strings weeks apart.
> 
> Also, there is the filename that indicates where we are already. Make these notes short and to the point?
> 
> Style is used for Cascading Style Sheets (CSS) rules.
> 

Done
Comment 153 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-07 10:04:10 PDT
Created attachment 537815 [details] [diff] [review]
Interdiff

I am able to upload an interdiff this time because the patch did not need to be rebased
Comment 154 Mihai Sucan [:msucan] 2011-06-08 11:00:11 PDT
Comment on attachment 537793 [details] [diff] [review]
CSSLogic + HtmlTree Upload 22

Review of attachment 537793 [details] [diff] [review]:
-----------------------------------------------------------------

The patch looks good, r+! Go ahead and ask a browser peer for review.


General comments:

- Open two pages and open the Web Console in each of these, then call inspectstyle(document.body) for each, then you see two style panels (good), then close one of the Web Consoles. Now all the style panels close. I would expect only the style panel opened from the Web Console I closed to close.

You can fix this issue easily: in HUDService.unregisterDisplay() do not call StyleInspector.closeAll(), In the JST_openStylePanel() function you can do styleInspector.panel.setAttribute("hudId", this.hudId). This is all you need to do, because in unregisterDisplay() we already have code that closes panels with the given hudId. Now style panels will close based on their origin.

- If you apply the above suggestion you can remove the StyleInspector.store.

- For some reason the panel resizer fails to work. I can't make the panel larger/thinner (I can't change the width, just the height). Might be a platform bug, or a bug caused by setting the panel width and height attributes. Maybe try sizeTo().

- One of the things I forgot in previous patch iterations: tests should use the Public Domain boilerplate.


Overall good work! Thanks for your patience with my review. :)

::: browser/base/content/csshtmltree.js
@@ +113,5 @@
> +  }
> +};
> +
> +/**
> + * Checks whether the UI is RTL

Get the user interface language direction.

@@ +114,5 @@
> +};
> +
> +/**
> + * Checks whether the UI is RTL
> + * @return {String} "ltr" or "rtl"

@return {string} The UI direction: "ltr" or "rtl".

@@ +116,5 @@
> +/**
> + * Checks whether the UI is RTL
> + * @return {String} "ltr" or "rtl"
> + */
> +CssHtmlTree.getRTLAttr = function CssHtmlTree_getRTLAttr()

This function gets the dir attribute value, rtl or ltr. Please rename the function accordingly.

@@ +125,5 @@
> +   } else {
> +     arguments.callee.dir = "ltr";
> +   }
> + }
> + return arguments.callee.dir;

I don't think it's good design to set arguments.callee.dir.

Just determine the UI direction and return the value.

@@ +129,5 @@
> + return arguments.callee.dir;
> +};
> +
> +/**
> + * Checks whether the UI is RTL

nit: a period at the end, please.

@@ +131,5 @@
> +
> +/**
> + * Checks whether the UI is RTL
> + * @return {Boolean} true or false
> + */

Booleans are always true or false :)

Please say something like:

@return {boolean} true if the the UI direction is right-to-left, or false otherwise.

@@ +974,5 @@
> +  },
> +
> +  /**
> +   * A localized Get localized human readable info
> +   */

The comment needs to be fixed, it seems.

Please also include the aElement argument and the @return info.

@@ +984,5 @@
> +      return this.text(aElement) + " \u2192 " + this.selectorInfo.value;
> +    }
> +  },
> +
> +  text: function(aElement) {

Please include a comment describing this method as well.

::: browser/base/content/styleinspector.js
@@ +50,5 @@
> +  prefEnabledName: "devtools.styleinspector.enabled",
> +
> +  /**
> +   * Is the Style Inspector enabled?
> +   * @returns {Boolean} true or false

Here and in other similar places please provide meaningful descriptions for @return.

@@ +122,5 @@
> +    }, false);
> +    panel.addEventListener("popuphidden", function SI_popup_shown() {
> +      StyleInspector.store.remove(self);
> +      Services.obs.notifyObservers(null, "styleinspector-closed", null);
> +    }, false);

I believe you need the accompanying removeEventListener() calls in each of these event handlers (popupshown and popuphidden).

@@ +124,5 @@
> +      StyleInspector.store.remove(self);
> +      Services.obs.notifyObservers(null, "styleinspector-closed", null);
> +    }, false);
> +
> +    let vbox = document.createElement("vbox");

For consistency please use createElementNS().

@@ +134,5 @@
> +    iframe.setAttribute("type", "chrome");
> +    iframe.setAttribute("tooltip", "aHTMLTooltip");
> +    vbox.appendChild(iframe);
> +
> +    let resizer = document.createElement("resizer");

createElementNS()

@@ +144,5 @@
> +
> +  /**
> +   * Show the panel.
> +   */
> +  show: function SI_show()

The entire method body is inside the if(!this.isOpen) {} block. You can move the code out, and just put if (!this.isOpen) { return; } at the beginning of the method.

@@ +205,5 @@
> +      Services.obs.addObserver(function() {
> +        Services.obs.removeObserver(arguments.callee, "styleinspector-opened", false);
> +        self.cssLogic.highlight(aNode);
> +        self.cssHtmlTree.highlight(aNode);
> +        Services.obs.notifyObservers(null, "styleinspector-ready", null);

Why do you send the styleinspector-ready notification here? It seems to be unused throughout the patch.

::: browser/base/content/test/browser_styleinspector.js
@@ +59,5 @@
> +    '<p>Inspect using inspectstyle(document.querySelectorAll("span")[0])</p>' +
> +    '</div>';
> +  doc.title = "Style Inspector Test";
> +  ok(window.StyleInspector, "styleInspector exists");
> +  styleInspector = new StyleInspector();

I think it would be better to have a clearer distinction here between the StyleInspector object and the instance (styleInspector), beyond the different case.

@@ +60,5 @@
> +    '</div>';
> +  doc.title = "Style Inspector Test";
> +  ok(window.StyleInspector, "styleInspector exists");
> +  styleInspector = new StyleInspector();
> +  ok(styleInspector instanceof StyleInspector, "style inspector instance creation");

This does not need to be tested, it's obvious from code construction.

::: toolkit/components/console/hudservice/tests/browser/Makefile.in
@@ +87,5 @@
>  	browser_webconsole_storage_iteration.js \
>  	browser_webconsole_storage_record_entry.js \
>  	browser_webconsole_storage_record_many_entries.js \
> +	browser_webconsole_styleinspector.js \
> +	browser_webconsole_styleinspector.htm \

I think we want browser_webconsole_styleinspector.html.

::: toolkit/components/console/hudservice/tests/browser/browser_webconsole_styleinspector.htm
@@ +1,3 @@
> +<!DOCTYPE HTML>
> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
> +  <title>Style inspector test</title>

This file needs the Public Domain boilerplate.

http://www.mozilla.org/MPL/boilerplate-1.1/

::: toolkit/components/console/hudservice/tests/browser/browser_webconsole_styleinspector.js
@@ +55,5 @@
> +  openConsole();
> +
> +  ok(window.StyleInspector, "styleInspector exists");
> +
> +  let hudId = HUDService.displaysIndex()[0];

displaysIndex() is deprecated.

Please use:

let hudId = HUDService.getHudIdByWindow(content);

@@ +59,5 @@
> +  let hudId = HUDService.displaysIndex()[0];
> +  let hud = HUDService.hudReferences[hudId];
> +  ok(hud, "we have a console");
> +
> +  hudBox = HUDService.getHeadsUpDisplay(hudId);

hudBox = hud.HUDBox
Comment 155 Mihai Sucan [:msucan] 2011-06-08 11:03:01 PDT
Comment on attachment 537815 [details] [diff] [review]
Interdiff

Thanks for the interdiff. It was really helpful!
Comment 156 Mihai Sucan [:msucan] 2011-06-08 11:29:42 PDT
Since this patch no longer technically depends on the highlighter patch, please remove the dependency and rebase accordingly (only a trivial change is needed).
Comment 157 :Ehsan Akhgari (busy, don't ask for review please) 2011-06-08 20:06:29 PDT
I downloaded the try build to see how this looks in RTL mode, but I couldn't figure out how to invoke the UI.  Selecting the Inspect item from the Web Developer menu shows the HTML inspector, and I can't see a CSS inspector at all...

But looking at the patch, why do you rely on template magics to stick in the right dir attributes?  You can just import chrome://global/locale/global.dtd and then use the &locale.dir; entity anywhere you need the direction value.
Comment 158 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-10 04:21:27 PDT
@ehsan: I will update the wiki page and create a video to make things easier. I actually have to rely on "template magic" for the xhtml page because of the way the templating system works. The XHTML is not valid and this breaks the &locale.dir; entity (switching it to LTR). We have another bug where we will evaluate templating systems.
Comment 159 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-10 05:24:12 PDT
Created attachment 538485 [details] [diff] [review]
Style Inspector complete patch

Removed highlighter and linked style inspector instances with web console instances.
Comment 160 :Ehsan Akhgari (busy, don't ask for review please) 2011-06-10 14:49:59 PDT
(In reply to comment #158)
> @ehsan: I will update the wiki page and create a video to make things
> easier. I actually have to rely on "template magic" for the xhtml page
> because of the way the templating system works. The XHTML is not valid and
> this breaks the &locale.dir; entity (switching it to LTR). We have another
> bug where we will evaluate templating systems.

OK.  Still, I'd also like to be able to test this in action before it lands...
Comment 161 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-11 06:50:10 PDT
@Ehsan: the latest try build is here:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mratcliffe@mozilla.com-f56f98cc7bd2/

To test it simply use e.g. inspectstyle(document.body) from the web console <ctrl><shift><k>.
Comment 162 :Ehsan Akhgari (busy, don't ask for review please) 2011-06-13 10:56:22 PDT
Looks really good to me RTL-wise!  :-)
Comment 163 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-17 05:43:15 PDT
I have created a video for QA:
http://people.mozilla.org/~mratcliffe/StyleInspector.avi
Comment 164 Axel Hecht [:Pike] 2011-06-20 04:33:55 PDT
(In reply to comment #163)
> I have created a video for QA:
> http://people.mozilla.org/~mratcliffe/StyleInspector.avi

Neither quicktime nor VLC play that video for me on the mac. Well, VLC plays something rather tilted and wrongly colored, that is.
Comment 165 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-22 06:28:25 PDT
I have converted the video to WebM format:
http://people.mozilla.org/~mratcliffe/StyleInspector.avi
Comment 166 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-22 06:29:09 PDT
I have converted the video to WebM format:
http://people.mozilla.org/~mratcliffe/StyleInspector.webm
Comment 167 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-22 06:29:50 PDT
I have converted the video to WebM format:
http://people.mozilla.org/~mratcliffe/StyleInspector.webm
Comment 168 Michael Ratcliffe [:miker] [:mratcliffe] 2011-06-27 01:30:13 PDT
Created attachment 542108 [details] [diff] [review]
Style Inspector complete patch 2
Comment 169 Justin Dolske [:Dolske] 2011-06-29 18:25:55 PDT
Comment on attachment 542108 [details] [diff] [review]
Style Inspector complete patch 2

Review of attachment 542108 [details] [diff] [review]:
-----------------------------------------------------------------

Sorry for the delay!

In addition to the review comments below, general question from back in comment 78:

----
I think you really need a way to dynamically get a list of all known-to-Gecko CSS properties (which I believe are common to all elements?), group the ones this code knows about, and then plop everything else into this "Other" group. Alternatively, write a test that looks at these tables and fails when an new (unknown) property is added. The problem here is that no one is going to remember to modify this code when new CSS properties are added in the future. So, either a test is needed to ensure people know to do that, or new properties should automatically show up somewhere generic (at which point users would presumably notice and file a bug to clean up the UI).
----

Did this ever get resolved?

Also, I haven't looked at the actual implementation of the CSS Inspector much yet, this is primarily a review of its touchpoints with the browser. I'll look at the rest in the next pass, which since it's already been reviewed shouldn't be too nitpicky. :)

::: browser/base/content/browser.js
@@ +181,5 @@
>  ];
>  
>  #include browser-fullZoom.js
>  #include inspector.js
> +#include styleinspector.js

Hmm. So, same concern as back in comment 51/64. I think the code in styleinspector.js (and the 2 files it #includes) should all be spun off into a .jsm.

Also, I think it would be highly desirable to have a "devtools" subdir to hold this and the other devtools code. Moving the existing should be done in a followup bug, I don't mind if you adjust the things added this patch or just do everything in the followup.

::: browser/base/content/csshtmltree.css
@@ +35,5 @@
> + * 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 ***** */
> +

Most, if not all, of this file should move to theme-specific files. (eg browser/themes/*/browser/devtools/). CSS in browser/content/ should basically only be used for things that are functional requirements, and not appearance. (EG, attaching XBL bindings, control of hidden/visible states, etc).

Also, has any of this been through a UX review? We should probably be doing that with devtools in general, though my more immediate concern is that the visual appearance (UI) is a little odd looking.

@@ +43,5 @@
> +  background: #EEE;
> +}
> +
> +.clearfix:after {
> +  content: ".";

Where does this "." show up?

@@ +71,5 @@
> +  background: -moz-linear-gradient(top, #F6F6FF, #E3E3FF);
> +  display: inline-block;
> +}
> +.path li:after {
> +  content: " > ";

Is this intended to look like (and thus imply) a CSS immediate-descendent operator? If not, probably want to use a different glyph here.

@@ +125,5 @@
> +.property-name, .property-value, .rule-count, .rule-unmatched {
> +  cursor: pointer;
> +}
> +
> +/* Take away these two :visited rules to get a core dumper */

Uhh, what?!

Please file a bug if you can reproduce, otherwise remove.

@@ +139,5 @@
> +}
> +.property-view[open="false"] .rule-count::after,
> +.property-view[open="false"] .rule-unmatched::after {
> +  content: " ▶";
> +}

Repeating my "Eww" from earlier review (see also bug 591651). :)

Please make these use images. We can sort out the exact styling in a followup, but the initial landing shouldn't be doing ascii-art UI.

::: browser/base/content/csshtmltree.js
@@ +1,1 @@
> +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

TODO, I haven't looked at this in detail yet but will for the next pass.

::: browser/base/content/csshtmltree.xhtml
@@ +1,1 @@
> +<!DOCTYPE html [

TODO, I haven't looked at this in detail yet but will for the next pass.

::: browser/base/content/csslogic.js
@@ +1,1 @@
> +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

TODO, I haven't looked at this in detail yet but will for the next pass.

::: browser/base/content/styleinspector.js
@@ +39,5 @@
> + * ***** END LICENSE BLOCK ***** */
> +#endif
> +
> +#include csslogic.js
> +#include csshtmltree.js

To expand on my .jsm wishes... These are pulling a whole lot of cruft into each browser window. And none of it is needed for the browser itself. Better to leave the browser window as unmolested as possible, and instead use JSMs to glue minimal, shared bits onto each <panel> on-demand.

@@ +42,5 @@
> +#include csslogic.js
> +#include csshtmltree.js
> +
> +function StyleInspector() {
> +  this._createPanel();

This is odd, see comment in HUDService.jsm.

I don't think you even need multiple StyleInspector objects. Just a single, static object (var StyleInspector = { ... }) on the browser window with createPanel() and a couple other bits for tracking. (As a JSM, you could even have a single instance shared across windows).

@@ +46,5 @@
> +  this._createPanel();
> +}
> +
> +StyleInspector.prototype = {
> +  prefEnabledName: "devtools.styleinspector.enabled",

You only use this in one place, just put the string there.

@@ +134,5 @@
> +    iframe.setAttribute("tooltip", "aHTMLTooltip");
> +    vbox.appendChild(iframe);
> +
> +    let hbox = document.createElement("hbox");
> +    hbox.setAttribute("style", "-moz-appearance: window;");

Aroo?

@@ +213,5 @@
> +        self.cssLogic.highlight(aNode);
> +        self.cssHtmlTree.highlight(aNode);
> +        Services.obs.notifyObservers(null, "styleinspector-ready", null);
> +      }, "styleinspector-opened", false);
> +      this.show();

This function also seems like a very odd flow.

Just create a new panel, stick aNode onto it, and let it initialize itself when it's ready (onpopupshown).

This will probably seem more obvious once you make csshtmltree.js/csslogic.js into JSM(s)... The basic flow would be:

  let panel = createElement(...);
  panel.CSSGoop = new CSSGoop(panel, aNode);

  from your jsm:

  function CSSGoop(panel, node) {
    this.node = node;
    this.panel = panel;
    this.preinit();
  }
  CSSGoop.proto {
    preinit : function() {
      this.panel.addEventListener("popupshowing", this.init);
    },
    init : function {
      removeEventListner() // maybe not even needed, but nice to do
      // fiddle with this.node, etc etc
    }
  }

@@ +222,5 @@
> +   * Toggle the style panel.
> +   *
> +   * @param [aNode] Node to select when opening panel
> +   */
> +  toggle: function SI_toggleStylePanel(aNode)

This doesn't seem to be called from anywhere. A couple other functions here look like they might be dead too?

::: browser/base/content/templater.js
@@ +1,1 @@
> +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

TODO, I haven't looked at this in detail yet but will for the next pass.

::: browser/locales/en-US/chrome/browser/styleinspector.dtd
@@ +1,4 @@
> +<!-- LOCALIZATION NOTE (style.lookingAtLabel): This is the label for the path of
> +  -  the highlighted element in the web page. This path is based on the document
> +  -  tree. -->
> +<!ENTITY style.lookingAtLabel                 "Looking at:">

Since you're only using these entities in you own windows, there's really no need to namespace them. I suggest dropping the leading "style.".

::: browser/locales/en-US/chrome/browser/styleinspector.properties
@@ +1,2 @@
> +# LOCALIZATION NOTE (style.panelTitle): This is the panel title
> +style.panelTitle=Style Inspector

Same as with the comment in the .dtd, I suggest you just drop the leading "style." from the property names.

Also, you can have a file-wide localization note, just add it to the top and drop the "(property.name)" part. That way each note doesn't have to repeat the stuff about this being the style panel, etc.

@@ +6,5 @@
> +# This is used inside the Style panel of the Inspector tool. For each style
> +# property the panel shows the number of rules which hold that specific
> +# property, counted from all of the stylesheet in the web page inspected.
> +style.property.numberOfRules=#1 rule;#1 rules
> +

Hmm, so this is an interesting file for localization. A number of these terms are fairly technical and have specific technical meaning, so at the very least I'd suspect we may want the localization notes to more firmly call this out. Maybe locales shouldn't even translate some of these, but I'll defer to L10N's opinion.

::: toolkit/components/console/hudservice/HUDService.jsm
@@ +4732,5 @@
> +   */
> +  openStylePanel: function JST_openStylePanel(aNode)
> +  {
> +    let doc = this.parentNode.ownerDocument;
> +    let styleInspector = new doc.defaultView.StyleInspector();

The styleInspector object lifetime seems very weird. You great a new object, call one function on it, and then it looks to be discarded to be GC'd. O_o

I think you really just want a single method ("static" in non-JS speak) that opens a new panel with a specified node.

EG:

  let styleInspector = this.parentNode.ownerDoc.defaultView.styleInspector;
  let panel = styleInspector.createPanel(aNode, true);
  panel.setAttribute("hudId", this.hudId);

@@ +4735,5 @@
> +    let doc = this.parentNode.ownerDocument;
> +    let styleInspector = new doc.defaultView.StyleInspector();
> +    styleInspector.panel.setAttribute("hudId", this.hudId);
> +
> +    if (styleInspector.isEnabled) {

Shouldn't this test wrap the entire function? IE, why create a StyleInspector and start initing the panel if you're not actually going to do anything?

@@ +4737,5 @@
> +    styleInspector.panel.setAttribute("hudId", this.hudId);
> +
> +    if (styleInspector.isEnabled) {
> +      if (!aNode || !(aNode instanceof Ci.nsIDOMNode) ||
> +          !(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration)) {

Split these up to have more specific error messages for each case?

A single "node is null or has no style" will be confusing when someone's "sure" they have a legit node, and are baffled as to how it might have no style.

Also, shouldn't this be up at the top of the function? That would also remove the oddity of creating a StyleInspector but then not using it.
Comment 170 Sebastian Zartner 2011-06-29 22:46:37 PDT
Mike, this issue is assigned to you. So I'm wondering, if it's not possible, that Bug 329509 gets more attention now, that an inspector will be integrated into Firefox.
Comment 171 Michael Ratcliffe [:miker] [:mratcliffe] 2011-07-06 02:04:50 PDT
Created attachment 544171 [details] [diff] [review]
Style Inspector complete patch 3

In addition to the review comments below, general question from back in comment 78:
>
>----
>I think you really need a way to dynamically get a list of all known-to-Gecko CSS properties (which I believe are common to all elements?), group the ones this code knows about, and then plop everything else into this "Other" group. Alternatively, write a test that looks at these tables and fails when an new (unknown) property is added. The problem here is that no one is going to remember to modify this code when new CSS properties are added in the future. So, either a test is needed to ensure people know to do that, or new properties should automatically show up somewhere generic (at which point users would presumably notice and file a bug to clean up the UI).
>----
>
>Did this ever get resolved?
>
>Also, I haven't looked at the actual implementation of the CSS Inspector much yet, this is primarily a review of its touchpoints with the browser. I'll look at the rest in the next pass, which since it's already been reviewed shouldn't be too nitpicky. :)
>

Yes, we are now getting the properties dynamically and any that we have not categorized are put in the "other" group. I don't think we should wait for bug reports in order to add new properties so I have added this to tests/browser_styleinspector.js which now fails if properties are not categorized (the properties are listed in the test log).

We previously had a bunch of unsupported properties in our property categories just in case support for them is added but due to the test this is no longer necessary so they have been removed.

>::: browser/base/content/browser.js
>@@ +181,5 @@
>>  ];
>>  
>>  #include browser-fullZoom.js
>>  #include inspector.js
>> +#include styleinspector.js
>
>Hmm. So, same concern as back in comment 51/64. I think the code in styleinspector.js (and the 2 files it #includes) should all be spun off into a .jsm.
>
>Also, I think it would be highly desirable to have a "devtools" subdir to hold this and the other devtools code. Moving the existing should be done in a followup bug, I don't mind if you adjust the things added this patch or just do everything in the followup.
>

New bug logged:
https://bugzilla.mozilla.org/show_bug.cgi?id=668493

>::: browser/base/content/csshtmltree.css
>@@ +35,5 @@
>> + * 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 ***** */
>> +
>
>Most, if not all, of this file should move to theme-specific files. (eg browser/themes/*/browser/devtools/). CSS in browser/content/ should basically only be used for things that are functional requirements, and not appearance. (EG, attaching XBL bindings, control of hidden/visible states, etc).
>

Moved

>Also, has any of this been through a UX review? We should probably be doing that with devtools in general, though my more immediate concern is that the visual appearance (UI) is a little odd looking.
>

Shall we put this in for UX review after your review is complete?

>@@ +43,5 @@
>> +  background: #EEE;
>> +}
>> +
>> +.clearfix:after {
>> +  content: ".";
>
>Where does this "." show up?
>

Historic code that I missed ... removed.

>@@ +71,5 @@
>> +  background: -moz-linear-gradient(top, #F6F6FF, #E3E3FF);
>> +  display: inline-block;
>> +}
>> +.path li:after {
>> +  content: " > ";
>
>Is this intended to look like (and thus imply) a CSS immediate-descendent operator? If not, probably want to use a different glyph here.
>

Yes, it is.

>@@ +125,5 @@
>> +.property-name, .property-value, .rule-count, .rule-unmatched {
>> +  cursor: pointer;
>> +}
>> +
>> +/* Take away these two :visited rules to get a core dumper */
>
>Uhh, what?!
>
>Please file a bug if you can reproduce, otherwise remove.
>

The planets all need to be aligned in a certain way ... create an html link in an iframe in a chrome document when running a debug build and bang! The workaround is to add a :visited rule to the stylesheet. A bug was logged for this issue a year ago (https://bugzilla.mozilla.org/show_bug.cgi?id=575675).

I have added a more descriptive comment to the stylesheets.

>@@ +139,5 @@
>> +}
>> +.property-view[open="false"] .rule-count::after,
>> +.property-view[open="false"] .rule-unmatched::after {
>> +  content: " ▶";
>> +}
>
>Repeating my "Eww" from earlier review (see also bug 591651). :)
>
>Please make these use images. We can sort out the exact styling in a followup, but the initial landing shouldn't be doing ascii-art UI.
>

Using unicode symbols allowed us to create arrows of any color and also add text effects to them ... anyhow, I have changed this to use a css sprite instead.

>::: browser/base/content/csshtmltree.js
>@@ +1,1 @@
>> +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
>
>TODO, I haven't looked at this in detail yet but will for the next pass.
>
>::: browser/base/content/csshtmltree.xhtml
>@@ +1,1 @@
>> +<!DOCTYPE html [
>
>TODO, I haven't looked at this in detail yet but will for the next pass.
>
>::: browser/base/content/csslogic.js
>@@ +1,1 @@
>> +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
>
>TODO, I haven't looked at this in detail yet but will for the next pass.
>
>::: browser/base/content/styleinspector.js
>@@ +39,5 @@
>> + * ***** END LICENSE BLOCK ***** */
>> +#endif
>> +
>> +#include csslogic.js
>> +#include csshtmltree.js
>
>To expand on my .jsm wishes... These are pulling a whole lot of cruft into each browser window. And none of it is needed for the browser itself. Better to leave the browser window as unmolested as possible, and instead use JSMs to glue minimal, shared bits onto each <panel> on-demand.
>

Agreed, as I mentioned earlier there is a new bug logged for this:
https://bugzilla.mozilla.org/show_bug.cgi?id=668493

>@@ +42,5 @@
>> +#include csslogic.js
>> +#include csshtmltree.js
>> +
>> +function StyleInspector() {
>> +  this._createPanel();
>
>This is odd, see comment in HUDService.jsm.
>
>I don't think you even need multiple StyleInspector objects. Just a single, static object (var StyleInspector = { ... }) on the browser window with createPanel() and a couple other bits for tracking. (As a JSM, you could even have a single instance shared across windows).
>

Okay

>@@ +46,5 @@
>> +  this._createPanel();
>> +}
>> +
>> +StyleInspector.prototype = {
>> +  prefEnabledName: "devtools.styleinspector.enabled",
>
>You only use this in one place, just put the string there.
>

Done

>@@ +134,5 @@
>> +    iframe.setAttribute("tooltip", "aHTMLTooltip");
>> +    vbox.appendChild(iframe);
>> +
>> +    let hbox = document.createElement("hbox");
>> +    hbox.setAttribute("style", "-moz-appearance: window;");
>
>Aroo?
>

Did I really do that? Fixed

>@@ +213,5 @@
>> +        self.cssLogic.highlight(aNode);
>> +        self.cssHtmlTree.highlight(aNode);
>> +        Services.obs.notifyObservers(null, "styleinspector-ready", null);
>> +      }, "styleinspector-opened", false);
>> +      this.show();
>
>This function also seems like a very odd flow.
>
>Just create a new panel, stick aNode onto it, and let it initialize itself when it's ready (onpopupshown).
>
>This will probably seem more obvious once you make csshtmltree.js/csslogic.js into JSM(s)... The basic flow would be:
>
>  let panel = createElement(...);
>  panel.CSSGoop = new CSSGoop(panel, aNode);
>
>  from your jsm:
>
>  function CSSGoop(panel, node) {
>    this.node = node;
>    this.panel = panel;
>    this.preinit();
>  }
>  CSSGoop.proto {
>    preinit : function() {
>      this.panel.addEventListener("popupshowing", this.init);
>    },
>    init : function {
>      removeEventListner() // maybe not even needed, but nice to do
>      // fiddle with this.node, etc etc
>    }
>  }
>

styleInspector now has one property for checking if the pref is enabled and one method, createPanel(). createPanel returns a panel with a couple of added methods, isOpen() & selectNode() and properties cssLogic & cssHtmlTree. Doing it this way there is no real need for tracking.

>@@ +222,5 @@
>> +   * Toggle the style panel.
>> +   *
>> +   * @param [aNode] Node to select when opening panel
>> +   */
>> +  toggle: function SI_toggleStylePanel(aNode)
>
>This doesn't seem to be called from anywhere. A couple other functions here look like they might be dead too?
>

Done

>::: browser/base/content/templater.js
>@@ +1,1 @@
>> +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
>
>TODO, I haven't looked at this in detail yet but will for the next pass.
>
>::: browser/locales/en-US/chrome/browser/styleinspector.dtd
>@@ +1,4 @@
>> +<!-- LOCALIZATION NOTE (style.lookingAtLabel): This is the label for the path of
>> +  -  the highlighted element in the web page. This path is based on the document
>> +  -  tree. -->
>> +<!ENTITY style.lookingAtLabel                 "Looking at:">
>
>Since you're only using these entities in you own windows, there's really no need to namespace them. I suggest dropping the leading "style.".
>

Done

>::: browser/locales/en-US/chrome/browser/styleinspector.properties
>@@ +1,2 @@
>> +# LOCALIZATION NOTE (style.panelTitle): This is the panel title
>> +style.panelTitle=Style Inspector
>
>Same as with the comment in the .dtd, I suggest you just drop the leading "style." from the property names.
>
>Also, you can have a file-wide localization note, just add it to the top and drop the "(property.name)" part. That way each note doesn't have to repeat the stuff about this being the style panel, etc.
>

Done

>@@ +6,5 @@
>> +# This is used inside the Style panel of the Inspector tool. For each style
>> +# property the panel shows the number of rules which hold that specific
>> +# property, counted from all of the stylesheet in the web page inspected.
>> +style.property.numberOfRules=#1 rule;#1 rules
>> +
>
>Hmm, so this is an interesting file for localization. A number of these terms are fairly technical and have specific technical meaning, so at the very least I'd suspect we may want the localization notes to more firmly call this out. Maybe locales shouldn't even translate some of these, but I'll defer to L10N's opinion.
>
>::: toolkit/components/console/hudservice/HUDService.jsm
>@@ +4732,5 @@
>> +   */
>> +  openStylePanel: function JST_openStylePanel(aNode)
>> +  {
>> +    let doc = this.parentNode.ownerDocument;
>> +    let styleInspector = new doc.defaultView.StyleInspector();
>
>The styleInspector object lifetime seems very weird. You great a new object, call one function on it, and then it looks to be discarded to be GC'd. O_o
>
>I think you really just want a single method ("static" in non-JS speak) that opens a new panel with a specified node.
>
>EG:
>
>  let styleInspector = this.parentNode.ownerDoc.defaultView.styleInspector;
>  let panel = styleInspector.createPanel(aNode, true);
>  panel.setAttribute("hudId", this.hudId);
>

Done

>@@ +4735,5 @@
>> +    let doc = this.parentNode.ownerDocument;
>> +    let styleInspector = new doc.defaultView.StyleInspector();
>> +    styleInspector.panel.setAttribute("hudId", this.hudId);
>> +
>> +    if (styleInspector.isEnabled) {
>
>Shouldn't this test wrap the entire function? IE, why create a StyleInspector and start initing the panel if you're not actually going to do anything?
>

Of course, done.

>@@ +4737,5 @@
>> +    styleInspector.panel.setAttribute("hudId", this.hudId);
>> +
>> +    if (styleInspector.isEnabled) {
>> +      if (!aNode || !(aNode instanceof Ci.nsIDOMNode) ||
>> +          !(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration)) {
>
>Split these up to have more specific error messages for each case?
>
>A single "node is null or has no style" will be confusing when someone's "sure" they have a legit node, and are baffled as to how it might have no style.
>

Done

>Also, shouldn't this be up at the top of the function? That would also remove the oddity of creating a StyleInspector but then not using it.

Yup, done
Comment 172 Michael Ratcliffe [:miker] [:mratcliffe] 2011-07-20 01:23:13 PDT
Created attachment 547005 [details] [diff] [review]
Style Inspector complete patch 4

Rebased
Comment 173 Rob Campbell [:rc] (:robcee) 2011-07-20 14:15:57 PDT
this doesn't apply cleanly. Looks like it's based on an out-of-date tree. (the test is going into browser/base/content/test instead of browser/base/content/test/inspector, for example).
Comment 174 Rob Campbell [:rc] (:robcee) 2011-07-20 14:24:34 PDT
there is also a segment failing to apply to HUDService.jsm I'm not sure is supposed to be there.

+    panels = popupset.querySelectorAll("panel[hudToolId=" + id + "]");
+    for (let i = 0; i < panels.length; i++) {
+      panels[i].hidePopup();
+      popupset.removeChild(panels[i]);
+    }
Comment 175 Rob Campbell [:rc] (:robcee) 2011-07-20 14:31:52 PDT
um, disregard that last content, though I'm still a bit puzzled why there are modifications to HUDService for this.
Comment 176 Rob Campbell [:rc] (:robcee) 2011-07-20 18:52:41 PDT
(In reply to comment #175)
> um, disregard that last content, though I'm still a bit puzzled why there
> are modifications to HUDService for this.

should have been "comment".

This doesn't appear to be using the registerTools API at all. Did that change?
Comment 177 Rob Campbell [:rc] (:robcee) 2011-07-20 19:00:37 PDT
and of course that's because I missed bug 663831. Don't worry, I'll decode this eventually...
Comment 178 Michael Ratcliffe [:miker] [:mratcliffe] 2011-07-21 03:10:31 PDT
> this doesn't apply cleanly. Looks like it's based on an out-of-date tree. (the test is going into browser/base/content/test instead of browser/base/content/test/inspector, for example).

It applies cleanly for me even on a brand new clean repo. Those tests are for the highlighter so I believe that browser/base/content/test is the correct directory

> there is also a segment failing to apply to HUDService.jsm I'm not sure is supposed to be there.
> 
> +    panels = popupset.querySelectorAll("panel[hudToolId=" + id + "]");
> +    for (let i = 0; i < panels.length; i++) {
> +      panels[i].hidePopup();
> +      popupset.removeChild(panels[i]);
> +    }

Applies cleanly for me. The modifications to HUDService add the inspectstyle(node) command (similar to the inspect() command). This particular section removes the style inspector from the DOM on console / tab close ready for garbage collection.

> um, disregard that last comment, though I'm still a bit puzzled why there are modifications to HUDService for this.

See my description above

> This doesn't appear to be using the registerTools API at all. Did that change?

This patch is the style inspector itself and it's integration with the HUD via inspectstyle(node) ... for the full flavor behaviour you will need:
Bug 660606 - Highlighter should allow registration of developer tools (I believe that you are planning on landing this soon)
Bug 663831 - Style inspector should be controllable from the highlighter

##################

Are you reviewing this now instead of Dolske?
Comment 179 Mihai Sucan [:msucan] 2011-07-21 05:19:05 PDT
(In reply to comment #174)
> there is also a segment failing to apply to HUDService.jsm I'm not sure is
> supposed to be there.
> 
> +    panels = popupset.querySelectorAll("panel[hudToolId=" + id + "]");
> +    for (let i = 0; i < panels.length; i++) {
> +      panels[i].hidePopup();
> +      popupset.removeChild(panels[i]);
> +    }

Mike: why not use the hudId attribute? add an event listener for popuphiding/popuphidden (depends what you want), that makes the style panel cleanup after itself.
Comment 180 Michael Ratcliffe [:miker] [:mratcliffe] 2011-07-21 05:47:21 PDT
(In reply to comment #179)
> (In reply to comment #174)
> > there is also a segment failing to apply to HUDService.jsm I'm not sure is
> > supposed to be there.
> > 
> > +    panels = popupset.querySelectorAll("panel[hudToolId=" + id + "]");
> > +    for (let i = 0; i < panels.length; i++) {
> > +      panels[i].hidePopup();
> > +      popupset.removeChild(panels[i]);
> > +    }
> 
> Mike: why not use the hudId attribute? add an event listener for
> popuphiding/popuphidden (depends what you want), that makes the style panel
> cleanup after itself.

Because I don't only want to hide it, I need to remove it from the DOM ready for GC, the hudId attribute is used when you only want to hide your panel but the hudToolId attribute is used to hide panels, then remove them from the DOM. Don't forget that you can open as many style inspector panels as you want.
Comment 181 Mihai Sucan [:msucan] 2011-07-21 05:48:58 PDT
(In reply to comment #180)
> (In reply to comment #179)
> > (In reply to comment #174)
> > > there is also a segment failing to apply to HUDService.jsm I'm not sure is
> > > supposed to be there.
> > > 
> > > +    panels = popupset.querySelectorAll("panel[hudToolId=" + id + "]");
> > > +    for (let i = 0; i < panels.length; i++) {
> > > +      panels[i].hidePopup();
> > > +      popupset.removeChild(panels[i]);
> > > +    }
> > 
> > Mike: why not use the hudId attribute? add an event listener for
> > popuphiding/popuphidden (depends what you want), that makes the style panel
> > cleanup after itself.
> 
> Because I don't only want to hide it, I need to remove it from the DOM ready
> for GC, the hudId attribute is used when you only want to hide your panel
> but the hudToolId attribute is used to hide panels, then remove them from
> the DOM. Don't forget that you can open as many style inspector panels as
> you want.

In the popuphidden event handler you can remove the panel from the DOM.
Comment 182 Michael Ratcliffe [:miker] [:mratcliffe] 2011-07-21 06:03:59 PDT
(In reply to comment #181)
> (In reply to comment #180)
> > (In reply to comment #179)
> > > (In reply to comment #174)
> > > > there is also a segment failing to apply to HUDService.jsm I'm not sure is
> > > > supposed to be there.
> > > > 
> > > > +    panels = popupset.querySelectorAll("panel[hudToolId=" + id + "]");
> > > > +    for (let i = 0; i < panels.length; i++) {
> > > > +      panels[i].hidePopup();
> > > > +      popupset.removeChild(panels[i]);
> > > > +    }
> > > 
> > > Mike: why not use the hudId attribute? add an event listener for
> > > popuphiding/popuphidden (depends what you want), that makes the style panel
> > > cleanup after itself.
> > 
> > Because I don't only want to hide it, I need to remove it from the DOM ready
> > for GC, the hudId attribute is used when you only want to hide your panel
> > but the hudToolId attribute is used to hide panels, then remove them from
> > the DOM. Don't forget that you can open as many style inspector panels as
> > you want.
> 
> In the popuphidden event handler you can remove the panel from the DOM.

But I only want to do that when the style inspector is launched from the HUD. When it is launched from the highlighter I don't remove it from the DOM because reusing panels improves performance.
Comment 183 Rob Campbell [:rc] (:robcee) 2011-07-21 07:21:44 PDT
no, I'm not really reviewing this, was just stumbling along trying to get this checked in on top of some other patches. I could review if it Mr. Dolske would like.

I'm not sure we really need this added to the webconsole anymore. I was under the impression that it was there initially as a boot-strapping mechanism while you figured out how to add it to the Highlighter. Not sure it makes sense being invokable from the console until we have a proper command line to do smart things like "css elementSelector".
Comment 184 Michael Ratcliffe [:miker] [:mratcliffe] 2011-07-21 08:44:37 PDT
(In reply to comment #183)
> no, I'm not really reviewing this, was just stumbling along trying to get
> this checked in on top of some other patches. I could review if it Mr.
> Dolske would like.
> 
> I'm not sure we really need this added to the webconsole anymore. I was
> under the impression that it was there initially as a boot-strapping
> mechanism while you figured out how to add it to the Highlighter. Not sure
> it makes sense being invokable from the console until we have a proper
> command line to do smart things like "css elementSelector".

There was already inspect(node) implemented that allows you to inspect nodes in the DOM Inspector so it made sense to add inspectstyle(node). At the moment this is the only way to launch 2 style inspectors so that the contents of both can be compared.
Comment 185 Michael Ratcliffe [:miker] [:mratcliffe] 2011-07-28 12:25:18 PDT
Created attachment 549194 [details] [diff] [review]
Style Inspector complete patch 5

Rebased
Comment 186 Justin Dolske [:Dolske] 2011-07-28 15:03:13 PDT
Comment on attachment 549194 [details] [diff] [review]
Style Inspector complete patch 5

Review of attachment 549194 [details] [diff] [review]:
-----------------------------------------------------------------

Couple of immediate comments as I've started looking over the latest patch...

More later.

::: browser/base/content/browser.js
@@ +181,5 @@
>  ];
>  
>  #include browser-fullZoom.js
>  #include inspector.js
> +#include styleinspector.js

So... I've mentioned a few times already that this is pulling an awful lot of stuff into the browser's scope, such that (1) the stuff #included by styleinspector.js should be refactored into a .jsm and (2) they probably shouldn't be browser.js globals at all (eg, #include or Cu.import() into on of the inspector windows, maybe?).

::: browser/base/content/browser.xul
@@ +54,5 @@
>  
>  <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
>  <?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
>  <?xml-stylesheet href="chrome://global/skin/webConsole.css" type="text/css"?>
> +<?xml-stylesheet href="chrome://browser/skin/csshtmltree.css" type="text/css"?>

The selectors used in csshtmltree.css are too generic for importing into the whole browser chrome. EG, anything in the browser chrome using <h1>, .path, or #header is probably going to get unexpected style changes.

Does this even _need_ to be in browser.xul? Can it just be pulled into csshtmltree.xhtml, so it only applies there?
Comment 187 Justin Dolske [:Dolske] 2011-07-29 19:57:02 PDT
Comment on attachment 549194 [details] [diff] [review]
Style Inspector complete patch 5

Review of attachment 549194 [details] [diff] [review]:
-----------------------------------------------------------------

Ok, I think I've made it through everything but csslogic.js (which I'll probably just skim), and templater.js (which had terrified me -- see comment 112 -- so I just need to satisfy myself that Joe's correct that it's fine. :)

Regarding UI review, I think I'd be OK if you at least got as far as starting to talking with UX about what to do with devtools UI reviews regarding this, existing, and future UI. UX really needs to get involved towards the _beginning_ of implementation, but here were are. Anything UX wants to change involves existing code now, so no point in holding up this landing.

::: browser/base/content/csshtmltree.js
@@ +81,5 @@
> +{
> +  if (!CssHtmlTree._strings) {
> +    CssHtmlTree._strings = Services.strings.createBundle(
> +        "chrome://browser/locale/styleinspector.properties");
> +  }

Could use XPCOMUtils.defineLazyGetter() here for ._strings. See mxr for examples.

@@ +99,5 @@
> + * @param {nsIDOMElement} aDestination the destination node where the
> + * processed nodes will be displayed.
> + * @param {object} aData the data to pass to the template.
> + */
> +CssHtmlTree.template = function CssHtmlTree_template(aTemplate, aDestination, aData)

nit: callers would read a little more naturally if this was a verb. doTemplate, processTemplate, appleTemplate, whatever.

@@ +106,5 @@
> +
> +  // All the templater does is to populate a given DOM tree with the given
> +  // values, so we need to clone the template first.
> +  let duplicated = aTemplate.cloneNode(true);
> +  new Templater().processNode(duplicated, aData);

That's kind of a weird pattern. Maybe this could just be a "static" method, just Templater.processNode() without the |new|?

@@ +121,5 @@
> +{
> +  if (typeof CssHtmlTree.getRTLAttr.navToolboxStyle == "undefined") {
> +    CssHtmlTree.getRTLAttr.navToolboxStyle = getComputedStyle(gNavToolbox);
> +  }
> +  return CssHtmlTree.getRTLAttr.navToolboxStyle.direction;

Could also use defineLazyGetter() here.

Although... It's a little weird to look at gNavToolbox for this. Just use |window|?

I'd also be curious to hear ehsans's thoughts on the right want to check "is Firefox in RTL mode". There are a lot of getComputedStyle() calls in browser.js that could probably be simplified... I suspect this function could just be removed entirely, and its callspots changed to just use CSS for directionality.

@@ +146,5 @@
> +    // Reset the style groups. Without this previously expanded groups
> +    // will fail to expand when inspecting subsequent nodes
> +    for (let i=0, numGroups=this.styleGroups.length; i < numGroups; i++) {
> +      this.styleGroups[i].reset(aElement ? false : true);
> +    }

Bonus points for:

  let close = !aElement;
  this.styleGroups.forEach(function(g) g.reset(close));

@@ +194,5 @@
> +   * Returns arrays of categorized properties.
> +   */
> +  _getPropertiesByGroup: function CssHtmlTree_getPropertiesByGroup()
> +  {
> +    return {

Doesn't seem like the callers need a unique copy of this table (right?). You could just make this a property on the prototype for less memory usage:

  propertiesByGroup : { text: [ ...], list: [...], ...},

If callers actually need a fresh copy to mutilate for their own needs, they can just make a duplicate (.concat()). I don't think that's needed often, if at all...

@@ +415,5 @@
> +          pbg.dims,
> +          pbg.pos,
> +          pbg.border,
> +          pbg.other
> +      );

mergedArray is never mutated... Just create this once at runtime.

Bonus followup bug: sort it, and then do a binary search here.

@@ +420,5 @@
> +
> +      // Here we build and cache a list of css properties supported by the browser
> +      // and store a list to check against. We could use any element but let's
> +      // use the inspector style panel
> +      let styles = window.getComputedStyle(window.document.documentElement);

This is a fair chunk of work to be doing, and the result is the same every time. Seems the whole function is that way.

The CssHtmlTree constructor should just do this once (or use a global in it's jsm scope, whatever).

@@ +490,5 @@
> +    // TODO: Animate opening/closing. See bug 587752.
> +    if (this.element.getAttribute("open") == "true") {
> +      this.element.setAttribute("open", "false");
> +      return;
> +    }

Consider changing this to just use the presence / absence of the "open" attribute.

Tends to make for simpler CSS selectors... EG .foo[open] instead of .foo[open=true].

::: browser/base/content/styleinspector.js
@@ +150,5 @@
> +    function l10n(aName)
> +    {
> +      if (!styleInspector._strings) {
> +        styleInspector._strings = Services.strings.createBundle(
> +            "chrome://browser/locale/styleinspector.properties");

More .defineLazyGetter()...

::: browser/base/content/templater.js
@@ +15,5 @@
> + *
> + * The Original Code is Bespin.
> + *
> + * The Initial Developer of the Original Code is
> + * Mozilla.

The Mozilla Foundation.
Comment 188 Joe Walker [:jwalker] (needinfo me or ping on irc) 2011-08-03 13:18:49 PDT
(In reply to comment #187)
> Comment on attachment 549194 [details] [diff] [review] [diff] [details] [review]
> @@ +106,5 @@
> > +
> > +  // All the templater does is to populate a given DOM tree with the given
> > +  // values, so we need to clone the template first.
> > +  let duplicated = aTemplate.cloneNode(true);
> > +  new Templater().processNode(duplicated, aData);
> 
> That's kind of a weird pattern. Maybe this could just be a "static" method,
> just Templater.processNode() without the |new|?

It could. There were 2 hold-ups:
- There was originally some good sense in having it non-static, but the theory didn't pan out. I wanted to check that it really could be removed seemlessly
- It's used in quite a few places, I wanted to make sure we moved them all at the same time, and avoid splintering this code.

I'd like to do this as part of bug 669486.
Comment 189 Axel Hecht [:Pike] 2011-08-08 06:19:28 PDT
Comment on attachment 549194 [details] [diff] [review]
Style Inspector complete patch 5

Also, please add a comment line like

# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals

to each entity that uses plurals. There are some source-level checks for plural forms that trigger on that comment. Sadly, there's no better heuristic than that reference to the plural docs. Precisely, it checks on 'Localization_and_Plurals' being in the comment before the entity.
Comment 190 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-09 09:34:45 PDT
Created attachment 551794 [details] [diff] [review]
Style Inspector complete patch 6

Review of attachment 549194 [details] [diff] [review] [diff] [details] [review]:
> -----------------------------------------------------------------
> 
> ::: browser/base/content/browser.js
> @@ +181,5 @@
> >  ];
> >  
> >  #include browser-fullZoom.js
> >  #include inspector.js
> > +#include styleinspector.js
> 
> So... I've mentioned a few times already that this is pulling an awful lot of stuff into the browser's scope, such that:
> 1. the stuff #included by styleinspector.js should be refactored into a .jsm
> 2. they probably shouldn't be browser.js globals at all (eg, #include or Cu.import() into on of the inspector windows, maybe?).
> 

Agreed ... we were planning on handling this as part of a separate bug but it looks like you really wanted it. All files including styleinspector.js are now jsm's and have been moved to the devtools folder. Shared modules have been moved to devtools/shared.

> ::: browser/base/content/browser.xul
> @@ +54,5 @@
> >  
> >  <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
> >  <?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
> >  <?xml-stylesheet href="chrome://global/skin/webConsole.css" type="text/css"?>
> > +<?xml-stylesheet href="chrome://browser/skin/csshtmltree.css" type="text/css"?>
> 
> The selectors used in csshtmltree.css are too generic for importing into the whole browser chrome. EG, anything in the browser chrome using <h1>, .path, or #header is probably going to get unexpected style changes.
> 
> Does this even _need_ to be in browser.xul? Can it just be pulled into csshtmltree.xhtml, so it only applies there?
> 

This entry must have been historic. Removed.

> Ok, I think I've made it through everything but csslogic.js (which I'll probably just skim), and templater.js (which had terrified me -- see comment 112 -- so I just need to satisfy myself that Joe's correct that it's fine. :)
> 
> Regarding UI review, I think I'd be OK if you at least got as far as starting to talking with UX about what to do with devtools UI reviews regarding this, existing, and future UI. UX really needs to get involved towards the _beginning_ of implementation, but here were are. Anything UX wants to change involves existing code now, so no point in holding up this landing.
> 

We discussed this with UX at the Fx work week. It turned out that Limi had already created a mockup of the style inspector so I have since implemented his design (Bug 672748: Style inspector UI refresh).

> ::: browser/base/content/csshtmltree.js
> @@ +81,5 @@
> > +{
> > +  if (!CssHtmlTree._strings) {
> > +    CssHtmlTree._strings = Services.strings.createBundle(
> > +        "chrome://browser/locale/styleinspector.properties");
> > +  }
> 
> Could use XPCOMUtils.defineLazyGetter() here for ._strings. See mxr for examples.
> 

defineLazyGetter eh? That was a new one on me ... like it, done.

> @@ +99,5 @@
> > + * @param {nsIDOMElement} aDestination the destination node where the
> > + * processed nodes will be displayed.
> > + * @param {object} aData the data to pass to the template.
> > + */
> > +CssHtmlTree.template = function CssHtmlTree_template(aTemplate, aDestination, aData)
> 
> nit: callers would read a little more naturally if this was a verb. doTemplate, processTemplate, appleTemplate, whatever.
> 

I prefer the name processTemplate so ... Done

> @@ +106,5 @@
> > +
> > +  // All the templater does is to populate a given DOM tree with the given
> > +  // values, so we need to clone the template first.
> > +  let duplicated = aTemplate.cloneNode(true);
> > +  new Templater().processNode(duplicated, aData);
> 
> That's kind of a weird pattern. Maybe this could just be a "static" method, just Templater.processNode() without the |new|?
> 

Feedback from Joe Walker:
It could. There were 2 hold-ups:
- There was originally some good sense in having it non-static, but the theory didn't pan out. I wanted to check that it really could be removed seemlessly
- It's used in quite a few places, I wanted to make sure we moved them all at the same time, and avoid splintering this code.

I'd like to do this as part of bug 669486.

> @@ +121,5 @@
> > +{
> > +  if (typeof CssHtmlTree.getRTLAttr.navToolboxStyle == "undefined") {
> > +    CssHtmlTree.getRTLAttr.navToolboxStyle = getComputedStyle(gNavToolbox);
> > +  }
> > +  return CssHtmlTree.getRTLAttr.navToolboxStyle.direction;
> 
> Could also use defineLazyGetter() here.
> 
> Although... It's a little weird to look at gNavToolbox for this. Just use |window|?
> 

I used gNavToolbox because this is used in at least one other place in Fx code. window has no style property so I have decided use gBrowser, which I think is more appropriate.
Changed and moved into a lazy getter.

> I'd also be curious to hear ehsans's thoughts on the right want to check "is Firefox in RTL mode".
> There are a lot of getComputedStyle() calls in browser.js that could probably be simplified... 
> I suspect this function could just be removed entirely, and its callspots changed to just use CSS for directionality.
> 

I have discussed this with Ehsan before. Ideally we would just use dir="rtl" or "dir="ltr" on document.body but this is just not possible without somehow detecting the direction first (and it doesn't work in our document for some unknown reason). In my opinion this should not be necessary ... if the browser is in RTL mode then any iframe in a panel should be rendered in rtl mode unless specifically overridden by dir="rtl." Also, whenever an xhtml document section is not valid then any further sections are rendered in ltr mode. I really need to create a bunch of test cases that illustrate these issues so that I can raise a decent bug at some point.

> @@ +146,5 @@
> > +    // Reset the style groups. Without this previously expanded groups
> > +    // will fail to expand when inspecting subsequent nodes
> > +    for (let i=0, numGroups=this.styleGroups.length; i < numGroups; i++) {
> > +      this.styleGroups[i].reset(aElement ? false : true);
> > +    }
> 
> Bonus points for:
> 
>   let close = !aElement;
>   this.styleGroups.forEach(function(g) g.reset(close));
> 

Okay, okay ... done.

> @@ +194,5 @@
> > +   * Returns arrays of categorized properties.
> > +   */
> > +  _getPropertiesByGroup: function CssHtmlTree_getPropertiesByGroup()
> > +  {
> > +    return {
> 
> Doesn't seem like the callers need a unique copy of this table (right?). You could just make this a property on the prototype for less memory usage:
> 
>   propertiesByGroup : { text: [ ...], list: [...], ...},
> 
> If callers actually need a fresh copy to mutilate for their own needs, they can just make a duplicate (.concat()). I don't think that's needed often, if at all...
> 

For test/browser/browser_styleinspector.js we need access to the table to check for any properties that are not covered by the style inspector (forcing the list to be up-to-date). Also, to be honest we remove categories in Bug 672743: Remove category view from the style inspector (we plan on landing these 2 bugs together) so all this code will be removed.

> @@ +415,5 @@
> > +          pbg.dims,
> > +          pbg.pos,
> > +          pbg.border,
> > +          pbg.other
> > +      );
> 
> mergedArray is never mutated... Just create this once at runtime.
> 
> Bonus followup bug: sort it, and then do a binary search here.
> 

It is only ever created once ... we remove categories in Bug 672743: Remove category view from the style inspector (we plan on landing these 2 bugs together) 

> @@ +420,5 @@
> > +
> > +      // Here we build and cache a list of css properties supported by the browser
> > +      // and store a list to check against. We could use any element but let's
> > +      // use the inspector style panel
> > +      let styles = window.getComputedStyle(window.document.documentElement);
> 
> This is a fair chunk of work to be doing, and the result is the same every time. Seems the whole function is that way.
> 
> The CssHtmlTree constructor should just do this once (or use a global in it's jsm scope, whatever).
> 

The method body is:
if (!CssHtmlTree.propertiesByGroup) {
  CssHtmlTree.propertiesByGroup = this._getPropertiesByGroup();
}
return CssHtmlTree.propertiesByGroup;

We do only do this once. this._getPropertiesByGroup() is cached in CssHtmlTree.propertiesByGroup. If CssHtmlTree.propertiesByGroup != this._getPropertiesByGroup() then we know that some new CSS property(s) were added to Fx (used in a test). We remove categories in Bug 672743: Remove category view from the style inspector (we plan on landing these 2 bugs together).

> @@ +490,5 @@
> > +    // TODO: Animate opening/closing. See bug 587752.
> > +    if (this.element.getAttribute("open") == "true") {
> > +      this.element.setAttribute("open", "false");
> > +      return;
> > +    }
> 
> Consider changing this to just use the presence / absence of the "open" attribute.
> 
> Tends to make for simpler CSS selectors... EG .foo[open] instead of .foo[open=true].
> 

Done

> ::: browser/base/content/styleinspector.js
> @@ +150,5 @@
> > +    function l10n(aName)
> > +    {
> > +      if (!styleInspector._strings) {
> > +        styleInspector._strings = Services.strings.createBundle(
> > +            "chrome://browser/locale/styleinspector.properties");
> 
> More .defineLazyGetter()...
> 

Done

> ::: browser/base/content/templater.js
> @@ +15,5 @@
> > + *
> > + * The Original Code is Bespin.
> > + *
> > + * The Initial Developer of the Original Code is
> > + * Mozilla.
> 
> The Mozilla Foundation.
> 
> 

Changed

(In reply to Axel Hecht [:Pike] from comment #189)
> Comment on attachment 549194 [details] [diff] [review] [diff] [details] [review]
> Style Inspector complete patch 5
> 
> Also, please add a comment line like
> 
> # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
> 
> to each entity that uses plurals. There are some source-level checks for
> plural forms that trigger on that comment. Sadly, there's no better
> heuristic than that reference to the plural docs. Precisely, it checks on
> 'Localization_and_Plurals' being in the comment before the entity.

Done
Comment 191 Dão Gottwald [:dao] 2011-08-11 11:02:20 PDT
Comment on attachment 551794 [details] [diff] [review]
Style Inspector complete patch 6

>+Cu.import("resource://gre/modules/styleinspector.jsm", devTools);

How about modules/devtools/styleinspector.jsm?

>diff --git a/browser/themes/gnomestripe/browser/arrows.png b/browser/themes/gnomestripe/browser/arrows.png

Please don't add this file as browser/themes/*stripe/browser/arrow.png.
We should probably create browser/themes/*stripe/devtools/ or at least browser/themes/*stripe/browser/devtools/.
Comment 192 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-14 10:21:59 PDT
Created attachment 552991 [details] [diff] [review]
Style Inspector complete patch 7

Rebased and updated according to Dao's comments
Comment 193 Justin Dolske [:Dolske] 2011-08-15 00:44:57 PDT
Comment on attachment 552991 [details] [diff] [review]
Style Inspector complete patch 7

Review of attachment 552991 [details] [diff] [review]:
-----------------------------------------------------------------

r- for a few things, but we're very very close!

::: browser/base/content/browser.js
@@ +190,5 @@
>  #include browser-syncui.js
>  #endif
>  
> +let devTools = {};
> +Cu.import("resource:///modules/devtools/styleinspector.jsm", devTools);

Is the plan to have more things using devTools.moreThings.stuff?

You should probably use a lazy getter here too, to entirely defer the Cu.import() until it's needed. [Have you run tryserver and checked Ts/Twinopen regressions? I suspect you'd have a small regression with the patches up to now, so I'm trying to save you some pain!]

See how AddonManager is defined, ~30 lines up. Except instead of memoizing on |this| (aka global scope), you'd do it on |devTools|.

Finally, convention seems to be for jsms to export Capital Symbols... s/styleinspector/StyleInspector/ in file name and exported symbol.

Maybe s/devTools/DevTools/ too? "devTools.StyleInspector.foo" feels a little weirder than "DevTools.StyleInspector.foo". Hmm, though really, is there much benefit of having the |devTools| object at all? Seems like alwaysing referring to devtools.foo.bar will be annoying, and everyone will start doing "var me = devtools.me" anyway... I'd be fine with just importing |StyleInspector| straight into the global scope here. The exported symbols stuff in JSMs give you the nice namespacing that you'd normally want the devTools object for...

::: browser/devtools/jar.mn
@@ +1,5 @@
>  browser.jar:
>      content/browser/NetworkPanel.xhtml            (webconsole/NetworkPanel.xhtml)
>  *   content/browser/scratchpad.xul                (scratchpad/scratchpad.xul)
>  *   content/browser/scratchpad.js                 (scratchpad/scratchpad.js)
> +    content/browser/csshtmltree.xhtml             (styleinspector/csshtmltree.xhtml)

I'd suggest having a "devtools" namespace here too. Either content/devtools/* or content/browser/devtools/*. But that would be fine to discuss in a followup.

::: browser/devtools/shared/templater.jsm
@@ +39,5 @@
> + * ***** END LICENSE BLOCK ***** */
> +
> +// WARNING: do not 'use_strict' without reading the notes in envEval;
> +
> +var EXPORTED_SYMBOLS = ["Templater"];

As noted in browser.js, convention is for this (and, well, all your other) JSMs to be named InterCaps.jsm, not lowercase.jsm.

::: browser/devtools/styleinspector/csshtmltree.jsm
@@ +66,5 @@
> +
> +  if (!CssHtmlTree.modules) {
> +    CssHtmlTree.modules = {};
> +    Cu.import("resource:///modules/devtools/csslogic.jsm", CssHtmlTree.modules);
> +    Cu.import("resource:///modules/templater.jsm", CssHtmlTree.modules);

Just import these into your modules scope (ala Services.jsm)? Or does external code need to be able to call CssHtmlTree.modules.Templater?

If the latter, I'd:

1) drop the intermediate |module| object (so, |CssHtmlTree.Templater|).
2) do |Cu.import("resource:///modules/templater.jsm", CssHtmlTree)| outside the constructor. Otherwise external code can't actually use .Templater et al until after it's done a |new CssHtmlTree|, which is likely to break something in subtle ways. And it's more consistent with other "static" functions like CssHtmlTree.l10n().

@@ +144,5 @@
> +    // will fail to expand when inspecting subsequent nodes
> +    let close = !aElement;
> +    this.styleGroups.forEach(function(group) {
> +      group.reset(close);
> +    });

Protip(tm): You don't actually need the braces around the function body here... 

https://developer.mozilla.org/en/New_in_JavaScript_1.8#Expression_closures_%28Merge_into_own_page.2fsection%29

Fine either way, though. ;-)

@@ +453,5 @@
> +
> +XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() {
> +  return Services.strings
> +      .createBundle("chrome://browser/locale/styleinspector.properties");
> +});

Nit: move this and getRTLAttr up with the other "static" functions before your prototype, so they're all together.

Could also drop braces here too... ;-)

   ..., function() Services.strings.createBundle("..."));

::: browser/devtools/styleinspector/csslogic.jsm
@@ +110,5 @@
> +  this._matchId = 0;
> +
> +  this._matchedSelectors = null;
> +  this._unmatchedSelectors = null;
> +

With the exception of |this._propertyInfos = {};|, all these initializations could be done in the prototype.

Maybe make a reset() method? cacheSheets() and hilight() both seem to have almost-identical code to do the same thing...

@@ +112,5 @@
> +  this._matchedSelectors = null;
> +  this._unmatchedSelectors = null;
> +
> +  this.domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].
> +      getService(Ci["inIDOMUtils"]);

Eww! Ci.foo, never Ci["foo"]. And, trivial nit, when doing Cc[...].getService(...), the "Cc" and "getService" should be aligned:

  var x = Cc[...].
          getService(...);

@@ +637,5 @@
> + */
> +CssLogic.l10n = function CssLogic_l10n(aName)
> +{
> +  // Alternative to using XPCOMUtils.defineLazyGetter - this keeps the l10n
> +  // code localized, is less code, and had less dependencies

I'd just nuke this comment.

@@ +641,5 @@
> +  // code localized, is less code, and had less dependencies
> +  if (!CssLogic._strings) {
> +    CssLogic._strings = Services.strings.createBundle(
> +        "chrome://browser/locale/styleinspector.properties");
> +  }

This is OK, but the one of the primary advantages of the defineLazyGetter / memoizing getters is you get more efficient code. IE, you only pay a construction penalty the first time the property is accessed, and further accesses don't waste cycles checking to see if it's initialized. Small win, but cheap.

@@ +662,5 @@
> +  let url = aSheet.href;
> +
> +  if (!url) return false;
> +  if (url.length === 0) return true;
> +  if (url[0] === 'h') return false;

Add comment; this is a shortcut for http[s]:, iirc?

::: browser/devtools/webconsole/test/browser/head.js
@@ +19,4 @@
>   * the Initial Developer. All Rights Reserved.
>   *
>   * Contributor(s):
> + *  Mike Ratcliffe <mratcliffe@mozilla.com>

Why the removal of the other 2 contributors?

::: browser/devtools/webconsole/HUDService.jsm
@@ +4724,5 @@
> +  {
> +    let module = {};
> +    Cu.import("resource:///modules/devtools/styleinspector.jsm", module);
> +    
> +    let styleInspector = module.styleInspector;

This is a bit odd. Just import() into HUDService.jsm's global scope (perhaps as a lazy getter), and that way you don't need to reimport it each time a panel is opened.

::: browser/installer/removed-files.in
@@ +943,5 @@
>    modules/HUDService.jsm
> +  modules/devtools/styleinspector.jsm
> +  modules/devtools/csshtmltree.jsm
> +  modules/devtools/csslogic.jsm
> +  modules/templater.jsm

Err, what? AFAIK we've never shipped these, so why are we deleting them?
Comment 194 Justin Dolske [:Dolske] 2011-08-15 00:53:40 PDT
Comment on attachment 552991 [details] [diff] [review]
Style Inspector complete patch 7

A couple other comments since Splinter seems very confused by the diffs for the Makefiles in your patch...

>diff --git a/browser/devtools/webconsole/Makefile.in b/browser/devtools/styleinspector/Makefile.in
>copy from browser/devtools/webconsole/Makefile.in
>copy to browser/devtools/styleinspector/Makefile.in
>--- a/browser/devtools/webconsole/Makefile.in
>+++ b/browser/devtools/styleinspector/Makefile.in
...
>-EXTRA_JS_MODULES = \
>-		HUDService.jsm \
>-		PropertyPanel.jsm \
>-		NetworkHelper.jsm \
>-		AutocompletePopup.jsm \
>-		gcli.jsm \
>-		$(NULL)
>-
...
>+
>+libs::
>+	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools

I _think_ you actually just want to list your JSM explicitly in EXTRA_JS_MODULES here, and not just blindly nsinstall it.

Might be good to have khuey take a quick look at the Makefile changes? [Is this patch building upon another one that's setting up browser/devtools, or are you doing it all here? It's a little confusing with the hg copy/renames, is there a repo/mxr that shows the final result on the tree? If not I guess I could just apply the patch(es) locally...]


>diff --git a/browser/devtools/webconsole/test/Makefile.in b/browser/devtools/styleinspector/test/browser/Makefile.in
>copy from browser/devtools/webconsole/test/Makefile.in
>copy to browser/devtools/styleinspector/test/browser/Makefile.in
>--- a/browser/devtools/webconsole/test/Makefile.in
>+++ b/browser/devtools/styleinspector/test/browser/Makefile.in
...
>+_BROWSER_TEST_FILES = \
>+  browser_styleinspector.js \
>+	browser_styleinspector_webconsole.js \
>+	head.js \
>+  $(NULL)

Just use 2-space indents on these lines, don't mix in tabs.


>+libs:: $(_BROWSER_TEST_FILES)
>+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
>+
>+libs:: $(_BROWSER_TEST_PAGES)
>+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

I think this can just be:

libs:: $(_BROWSER_TEST_FILES) $(_BROWSER_TEST_PAGES)
    $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
Comment 195 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-19 04:08:05 PDT
Created attachment 554358 [details] [diff] [review]
Style Inspector complete patch 8

> ::: browser/base/content/browser.js
> @@ +190,5 @@
> >  #include browser-syncui.js
> >  #endif
> >  
> > +let devTools = {};
> > +Cu.import("resource:///modules/devtools/styleinspector.jsm", devTools);
> 
> Is the plan to have more things using devTools.moreThings.stuff?
> 

No ... I will just import into the global namespace as you mention a couple of comments down.

> You should probably use a lazy getter here too, to entirely defer the
> Cu.import() until it's needed. [Have you run tryserver and checked
> Ts/Twinopen regressions? I suspect you'd have a small regression with the
> patches up to now, so I'm trying to save you some pain!]
> 
> See how AddonManager is defined, ~30 lines up. Except instead of memoizing
> on |this| (aka global scope), you'd do it on |devTools|.
> 

The style inspector needs to register itself with the highlighter onload in bug 663831 so making it a lazy getter here would not make sense. I have changed it to use global scope though. I have also made CssLogic and CssHtmlTree use lazy loaders.

> Finally, convention seems to be for jsms to export Capital Symbols...
> s/styleinspector/StyleInspector/ in file name and exported symbol.
> 

Done

> Maybe s/devTools/DevTools/ too? "devTools.StyleInspector.foo" feels a little
> weirder than "DevTools.StyleInspector.foo". Hmm, though really, is there
> much benefit of having the |devTools| object at all? Seems like alwaysing
> referring to devtools.foo.bar will be annoying, and everyone will start
> doing "var me = devtools.me" anyway... I'd be fine with just importing
> |StyleInspector| straight into the global scope here. The exported symbols
> stuff in JSMs give you the nice namespacing that you'd normally want the
> devTools object for...
> 

Done (I used global scope)

> ::: browser/devtools/jar.mn
> @@ +1,5 @@
> >  browser.jar:
> >      content/browser/NetworkPanel.xhtml            (webconsole/NetworkPanel.xhtml)
> >  *   content/browser/scratchpad.xul                (scratchpad/scratchpad.xul)
> >  *   content/browser/scratchpad.js                 (scratchpad/scratchpad.js)
> > +    content/browser/csshtmltree.xhtml             (styleinspector/csshtmltree.xhtml)
> 
> I'd suggest having a "devtools" namespace here too. Either
> content/devtools/* or content/browser/devtools/*. But that would be fine to
> discuss in a followup.
> 

Bug 679364 - Use either content/devtools/* or content/browser/devtools/* for aliases

> ::: browser/devtools/shared/templater.jsm
> @@ +39,5 @@
> > + * ***** END LICENSE BLOCK ***** */
> > +
> > +// WARNING: do not 'use_strict' without reading the notes in envEval;
> > +
> > +var EXPORTED_SYMBOLS = ["Templater"];
> 
> As noted in browser.js, convention is for this (and, well, all your other)
> JSMs to be named InterCaps.jsm, not lowercase.jsm.
> 

camelCase it is then ... done.

> ::: browser/devtools/styleinspector/csshtmltree.jsm
> @@ +66,5 @@
> > +
> > +  if (!CssHtmlTree.modules) {
> > +    CssHtmlTree.modules = {};
> > +    Cu.import("resource:///modules/devtools/csslogic.jsm", CssHtmlTree.modules);
> > +    Cu.import("resource:///modules/templater.jsm", CssHtmlTree.modules);
> 
> Just import these into your modules scope (ala Services.jsm)? Or does
> external code need to be able to call CssHtmlTree.modules.Templater?
> 

Done

> 
> @@ +144,5 @@
> > +    // will fail to expand when inspecting subsequent nodes
> > +    let close = !aElement;
> > +    this.styleGroups.forEach(function(group) {
> > +      group.reset(close);
> > +    });
> 
> Protip(tm): You don't actually need the braces around the function body
> here... 
> 
> https://developer.mozilla.org/en/New_in_JavaScript_1.
> 8#Expression_closures_%28Merge_into_own_page.2fsection%29
> 
> Fine either way, though. ;-)
> 

I know ... I really dislike the syntax for doing that but I guess I should use it, maybe it is because I have resisted using it so far.
Done.

> @@ +453,5 @@
> > +
> > +XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() {
> > +  return Services.strings
> > +      .createBundle("chrome://browser/locale/styleinspector.properties");
> > +});
> 
> Nit: move this and getRTLAttr up with the other "static" functions before
> your prototype, so they're all together.
> 

Done

> Could also drop braces here too... ;-)
> 
>    ..., function() Services.strings.createBundle("..."));
> 

Done in a few places

> ::: browser/devtools/styleinspector/csslogic.jsm
> @@ +110,5 @@
> > +  this._matchId = 0;
> > +
> > +  this._matchedSelectors = null;
> > +  this._unmatchedSelectors = null;
> > +
> 
> With the exception of |this._propertyInfos = {};|, all these initializations
> could be done in the prototype.
> 

_propertyInfos can be initialized in the prototype as well. Done.

> Maybe make a reset() method? cacheSheets() and hilight() both seem to have
> almost-identical code to do the same thing...
> 

Done

> @@ +112,5 @@
> > +  this._matchedSelectors = null;
> > +  this._unmatchedSelectors = null;
> > +
> > +  this.domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].
> > +      getService(Ci["inIDOMUtils"]);
> 
> Eww! Ci.foo, never Ci["foo"].

Changed

> And, trivial nit, when doing
> Cc[...].getService(...), the "Cc" and "getService" should be aligned:
> 
>   var x = Cc[...].
>           getService(...);
> 

It is now under 80 characters so I guess this no longer applies.

> @@ +637,5 @@
> > + */
> > +CssLogic.l10n = function CssLogic_l10n(aName)
> > +{
> > +  // Alternative to using XPCOMUtils.defineLazyGetter - this keeps the l10n
> > +  // code localized, is less code, and had less dependencies
> 
> I'd just nuke this comment.
> 

Done

> @@ +641,5 @@
> > +  // code localized, is less code, and had less dependencies
> > +  if (!CssLogic._strings) {
> > +    CssLogic._strings = Services.strings.createBundle(
> > +        "chrome://browser/locale/styleinspector.properties");
> > +  }
> 
> This is OK, but the one of the primary advantages of the defineLazyGetter /
> memoizing getters is you get more efficient code. IE, you only pay a
> construction penalty the first time the property is accessed, and further
> accesses don't waste cycles checking to see if it's initialized. Small win,
> but cheap.
> 

Agreed, moved into a lazy getter

> @@ +662,5 @@
> > +  let url = aSheet.href;
> > +
> > +  if (!url) return false;
> > +  if (url.length === 0) return true;
> > +  if (url[0] === 'h') return false;
> 
> Add comment; this is a shortcut for http[s]:, iirc?
> 

Done

> ::: browser/devtools/webconsole/test/browser/head.js
> @@ +19,4 @@
> >   * the Initial Developer. All Rights Reserved.
> >   *
> >   * Contributor(s):
> > + *  Mike Ratcliffe <mratcliffe@mozilla.com>
> 
> Why the removal of the other 2 contributors?
> 

This is probably clearer if you look at more of the patch data:
diff --git a/browser/devtools/webconsole/test/browser/head.js b/browser/devtools/styleinspector/test/browser/head.js
copy from browser/devtools/webconsole/test/browser/head.js
copy to browser/devtools/styleinspector/test/browser/head.js
--- a/browser/devtools/webconsole/test/browser/head.js
+++ b/browser/devtools/styleinspector/test/browser/head.js

I copied the file to a new location and then deleted the contributors in the new file (not the old one). I guess it would have been less confusing if I manually created the file from scratch. I have now changed all "copied" files from the patch so that they look like newly created files.

> ::: browser/devtools/webconsole/HUDService.jsm
> @@ +4724,5 @@
> > +  {
> > +    let module = {};
> > +    Cu.import("resource:///modules/devtools/styleinspector.jsm", module);
> > +    
> > +    let styleInspector = module.styleInspector;
> 
> This is a bit odd. Just import() into HUDService.jsm's global scope (perhaps
> as a lazy getter), and that way you don't need to reimport it each time a
> panel is opened.

I am starting to see a lazy getter theme here ;o)
done

> ::: browser/installer/removed-files.in
> @@ +943,5 @@
> >    modules/HUDService.jsm
> > +  modules/devtools/styleinspector.jsm
> > +  modules/devtools/csshtmltree.jsm
> > +  modules/devtools/csslogic.jsm
> > +  modules/templater.jsm
> 
> Err, what? AFAIK we've never shipped these, so why are we deleting them?

Fixed

> >diff --git a/browser/devtools/webconsole/Makefile.in b/browser/devtools/styleinspector/Makefile.in
> >copy from browser/devtools/webconsole/Makefile.in
> >copy to browser/devtools/styleinspector/Makefile.in
> >--- a/browser/devtools/webconsole/Makefile.in
> >+++ b/browser/devtools/styleinspector/Makefile.in
> ...
> >-EXTRA_JS_MODULES = \
> >-		HUDService.jsm \
> >-		PropertyPanel.jsm \
> >-		NetworkHelper.jsm \
> >-		AutocompletePopup.jsm \
> >-		gcli.jsm \
> >-		$(NULL)
> >-
> ...
> >+
> >+libs::
> >+	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
> 
> I _think_ you actually just want to list your JSM explicitly in
> EXTRA_JS_MODULES here, and not just blindly nsinstall it.
> 
> Might be good to have khuey take a quick look at the Makefile changes?

I spoke to khuey about this and he said that because our modules need to be accessible from resource:///modules/devtools/module.jsm (as requested by Däo) we can't do it using EXTRA_JS_MODULES and my code is the way to do it.

> Is this patch building upon another one that's setting up browser/devtools, or
> are you doing it all here?

browser/devtools was already set up in bug 579909 ... I have included that bug as a dependency to help Splinter.

> It's a little confusing with the hg copy/renames,
> is there a repo/mxr that shows the final result on the tree? If not I guess
> I could just apply the patch(es) locally...]
> 

I didn't realize that it would be so confusing but I have now made all the copied files look like freshly created files so it is easier to follow now.

Time for an ASCII art tree ;o)

browser/devtools/styleinspector
|-- csshtmltree.jsm
|-- csshtmltree.xhtml
|-- csslogic.jsm
|-- Makefile.in
|-- styleinspector.jsm
`-- test
    |-- browser
    |   |-- browser_styleinspector.js
    |   |-- browser_styleinspector_webconsole.htm
    |   |-- browser_styleinspector_webconsole.js
    |   |-- head.js
    |   `-- Makefile.in
    `-- Makefile.in

browser/themes/*stripe/browser/devtools
|-- arrows.png
`-- csshtmltree.css

> >diff --git a/browser/devtools/webconsole/test/Makefile.in b/browser/devtools/styleinspector/test/browser/Makefile.in
> >copy from browser/devtools/webconsole/test/Makefile.in
> >copy to browser/devtools/styleinspector/test/browser/Makefile.in
> >--- a/browser/devtools/webconsole/test/Makefile.in
> >+++ b/browser/devtools/styleinspector/test/browser/Makefile.in
> ...
> >+_BROWSER_TEST_FILES = \
> >+  browser_styleinspector.js \
> >+	browser_styleinspector_webconsole.js \
> >+	head.js \
> >+  $(NULL)
> 
> Just use 2-space indents on these lines, don't mix in tabs.
> 

Done

> >+libs:: $(_BROWSER_TEST_FILES)
> >+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
> >+
> >+libs:: $(_BROWSER_TEST_PAGES)
> >+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
> 
> I think this can just be:
> 
> libs:: $(_BROWSER_TEST_FILES) $(_BROWSER_TEST_PAGES)
>     $(INSTALL) $(foreach f,$^,"$f")
> $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

I had tried that and it doesn't work. It is done in the other way in all makefiles I have looked at.
Comment 196 Justin Dolske [:Dolske] 2011-08-23 18:28:05 PDT
Comment on attachment 554358 [details] [diff] [review]
Style Inspector complete patch 8

Review of attachment 554358 [details] [diff] [review]:
-----------------------------------------------------------------

So close! So very very close! Will review a followup asap!

::: browser/base/content/browser.js
@@ +192,5 @@
>  
> +// We do not use a lazy getter here as the style inspector needs to use onload
> +// event to register itself with the highlighter (bug 663831)
> +Cu.import("resource:///modules/devtools/StyleInspector.jsm");
> +

*frowny face*

We should really be trying to avoid as much work as possible when opening a new window. Can we make this a lazy-getter here, and sort out the issues in bug 663831? I'd rather un-lazy it later, instead of using followups to fix likely performance regressions.

::: browser/devtools/styleinspector/CssHtmlTree.jsm
@@ +67,5 @@
> +  if (!CssHtmlTree.modules) {
> +    CssHtmlTree.modules = {};
> +    Cu.import("resource:///modules/devtools/CssLogic.jsm", CssHtmlTree.modules);
> +    Cu.import("resource:///modules/Templater.jsm", CssHtmlTree.modules);
> +  }

These were meant to move to the global scope, no? Or Cu.import() them into other things accessing CssHtmlTree.modules.*?

::: browser/devtools/styleinspector/CssLogic.jsm
@@ +132,5 @@
> +  // The total number of rules, in all stylesheets, after filtering.
> +  _ruleCount: 0,
> +
> +  // The cache of examined CSS properties.
> +  _propertyInfos: {},

Well, you need to be careful with objects in the prototype, since they'll often bite you unexpectedly if you're not expecting them to be shared.

EG
  let foo = new CssLogic();
  let bar = new CssLogic();
  foo._propertyInfos.lolcat  = ":-3";
  bar._propertyInfos.lolcat == ":-3"; // true!

Pretty sure you don't want/need that "feature" here, so safer to give each instance a unique _propertyInfos in its constructor.

@@ +174,5 @@
> +  {
> +    if (!aViewedElement) {
> +      this.viewedElement = null;
> +      this.viewedDocument = null;
> +      this._sheets = null;

null here...

@@ +176,5 @@
> +      this.viewedElement = null;
> +      this.viewedDocument = null;
> +      this._sheets = null;
> +      this._computedStyle = null;
> +      this.reset();

this._sheetIndex unchanged here...

@@ +276,5 @@
> +   * @private
> +   */
> +  _cacheSheets: function CssLogic_cacheSheets()
> +  {
> +    this._sheets = {};

...but new object here?

@@ +278,5 @@
> +  _cacheSheets: function CssLogic_cacheSheets()
> +  {
> +    this._sheets = {};
> +    this._passId++;
> +    this._sheetIndex = 0;

...but set to 0 here?

@@ +279,5 @@
> +  {
> +    this._sheets = {};
> +    this._passId++;
> +    this._sheetIndex = 0;
> +    this.reset();

Seems like reset() should handle both of these?

::: browser/devtools/styleinspector/StyleInspector.jsm
@@ +44,5 @@
> +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
> +
> +var EXPORTED_SYMBOLS = ["StyleInspector"];
> +
> +let win = Services.wm.getMostRecentWindow("navigator:browser");

Nothing's using this except for createPanel(), move it there?

Having this is the module's global scope would be really weird, because it's only the most-recent window for the point in time when something first imports StyleInspector.jsm.

::: browser/devtools/webconsole/HUDService.jsm
@@ +4733,5 @@
> +   *        Object to display/inspect inside of the tree.
> +   */
> +  openStylePanel: function JST_openStylePanel(aNode)
> +  {
> +    if (StyleInspector.isEnabled) {

Consider moving this up into aJSTerm.sandbox.inspectstyle()?

As is, if the inspector is disabled, "inspectstyle foo" will just silently do nothing. It would seem to make more sense to have it explicitly say it's disabled?

Though I guess if this is just your Rapid-Release Kill-o-Switch it doesn't really matter.
Comment 197 Dão Gottwald [:dao] 2011-08-24 01:23:47 PDT
Comment on attachment 554358 [details] [diff] [review]
Style Inspector complete patch 8

>diff --git a/browser/devtools/styleinspector/StyleInspector.jsm b/browser/devtools/styleinspector/StyleInspector.jsm

>+let win = Services.wm.getMostRecentWindow("navigator:browser");

This looks entirely wrong. How is this supposed to handle the style inspector being invoked in a second browser window? When you import a JS module, you always get the same entity, not separate copies.
Comment 198 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-24 05:07:27 PDT
Created attachment 555365 [details] [diff] [review]
Style Inspector complete patch 9

I knew the promise of bacon would do it ;o)

I have now addressed your last few concerns.
Comment 199 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-24 05:08:55 PDT
Dao's Feedback:
> Comment on attachment 554358 [details] [diff] [review]
> Style Inspector complete patch 8
> 
> >diff --git a/browser/devtools/styleinspector/StyleInspector.jsm b/browser/devtools/styleinspector/StyleInspector.jsm
> 
> >+let win = Services.wm.getMostRecentWindow("navigator:browser");
> 
> This looks entirely wrong. How is this supposed to handle the style
> inspector being invoked in a second browser window? When you import a JS
> module, you always get the same entity, not separate copies.

Of course ... done.

Dolske's Feedback:

> ::: browser/base/content/browser.js
> @@ +192,5 @@
> >  
> > +// We do not use a lazy getter here as the style inspector needs to use onload
> > +// event to register itself with the highlighter (bug 663831)
> > +Cu.import("resource:///modules/devtools/StyleInspector.jsm");
> > +
> 
> *frowny face*
> 
> We should really be trying to avoid as much work as possible when opening a
> new window. Can we make this a lazy-getter here, and sort out the issues in
> bug 663831? I'd rather un-lazy it later, instead of using followups to fix
> likely performance regressions.
> 

Sure, I have a better plan on how to register tools anyhow (I never liked the current hack)

> ::: browser/devtools/styleinspector/CssHtmlTree.jsm
> @@ +67,5 @@
> > +  if (!CssHtmlTree.modules) {
> > +    CssHtmlTree.modules = {};
> > +    Cu.import("resource:///modules/devtools/CssLogic.jsm", CssHtmlTree.modules);
> > +    Cu.import("resource:///modules/Templater.jsm", CssHtmlTree.modules);
> > +  }
> 
> These were meant to move to the global scope, no? Or Cu.import() them into
> other things accessing CssHtmlTree.modules.*?
> 

Not sure how I missed them, done.

> ::: browser/devtools/styleinspector/CssLogic.jsm
> @@ +132,5 @@
> > +  // The total number of rules, in all stylesheets, after filtering.
> > +  _ruleCount: 0,
> > +
> > +  // The cache of examined CSS properties.
> > +  _propertyInfos: {},
> 
> Well, you need to be careful with objects in the prototype, since they'll
> often bite you unexpectedly if you're not expecting them to be shared.
> 
> EG
>   let foo = new CssLogic();
>   let bar = new CssLogic();
>   foo._propertyInfos.lolcat  = ":-3";
>   bar._propertyInfos.lolcat == ":-3"; // true!
> 
> Pretty sure you don't want/need that "feature" here, so safer to give each
> instance a unique _propertyInfos in its constructor.
> 

Yeah, it works here but you are right ... done.

> @@ +174,5 @@
> > +  {
> > +    if (!aViewedElement) {
> > +      this.viewedElement = null;
> > +      this.viewedDocument = null;
> > +      this._sheets = null;
> 
> null here...
> 
> @@ +276,5 @@
> > +   * @private
> > +   */
> > +  _cacheSheets: function CssLogic_cacheSheets()
> > +  {
> > +    this._sheets = {};
> 
> ...but new object here?
> 

This did appear strange but was necessary due to the way CssLogic has been written. To tidy this up I have had to introduce a _sheetsCached property ... done.

> @@ +176,5 @@
> > +      this.viewedElement = null;
> > +      this.viewedDocument = null;
> > +      this._sheets = null;
> > +      this._computedStyle = null;
> > +      this.reset();
> 
> this._sheetIndex unchanged here...
> 
> @@ +278,5 @@
> > +  _cacheSheets: function CssLogic_cacheSheets()
> > +  {
> > +    this._sheets = {};
> > +    this._passId++;
> > +    this._sheetIndex = 0;
> 
> ...but set to 0 here?
> 
> @@ +279,5 @@
> > +  {
> > +    this._sheets = {};
> > +    this._passId++;
> > +    this._sheetIndex = 0;
> > +    this.reset();
> 
> Seems like reset() should handle both of these?
> 

Done

> ::: browser/devtools/styleinspector/StyleInspector.jsm
> @@ +44,5 @@
> > +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
> > +
> > +var EXPORTED_SYMBOLS = ["StyleInspector"];
> > +
> > +let win = Services.wm.getMostRecentWindow("navigator:browser");
> 
> Nothing's using this except for createPanel(), move it there?
> 
> Having this is the module's global scope would be really weird, because it's
> only the most-recent window for the point in time when something first
> imports StyleInspector.jsm.
> 

Done

> ::: browser/devtools/webconsole/HUDService.jsm
> @@ +4733,5 @@
> > +   *        Object to display/inspect inside of the tree.
> > +   */
> > +  openStylePanel: function JST_openStylePanel(aNode)
> > +  {
> > +    if (StyleInspector.isEnabled) {
> 
> Consider moving this up into aJSTerm.sandbox.inspectstyle()?
> 

Done

> As is, if the inspector is disabled, "inspectstyle foo" will just silently
> do nothing. It would seem to make more sense to have it explicitly say it's
> disabled?
> 
> Though I guess if this is just your Rapid-Release Kill-o-Switch it doesn't
> really matter.

It is the Rapid-Release Kill-o-Switch but I think a little feedback == more discoverable == good. Done.
Comment 200 Dão Gottwald [:dao] 2011-08-24 05:17:55 PDT
Comment on attachment 555365 [details] [diff] [review]
Style Inspector complete patch 9

>+Cu.import("resource:///modules/devtools/CssLogic.jsm");
>+Cu.import("resource:///modules/Templater.jsm");

As I mentioned in the newsgroups, I don't understand why Templater.jsm isn't packaged in modules/devtools/. devtools shouldn't expose stuff to the general public just because somebody might find it handy. If this is really supposed to be used outside of devtools, it should be somewhere in toolkit rather than browser/devtools/.
Comment 201 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-24 06:00:55 PDT
(In reply to Dão Gottwald [:dao] from comment #200)
> Comment on attachment 555365 [details] [diff] [review]
> Style Inspector complete patch 9
> 
> >+Cu.import("resource:///modules/devtools/CssLogic.jsm");
> >+Cu.import("resource:///modules/Templater.jsm");
> 
> As I mentioned in the newsgroups, I don't understand why Templater.jsm isn't
> packaged in modules/devtools/. devtools shouldn't expose stuff to the
> general public just because somebody might find it handy. If this is really
> supposed to be used outside of devtools, it should be somewhere in toolkit
> rather than browser/devtools/.

Done
Comment 202 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-24 06:02:09 PDT
Created attachment 555379 [details] [diff] [review]
Style Inspector complete patch 10
Comment 203 Justin Dolske [:Dolske] 2011-08-24 12:30:13 PDT
Comment on attachment 555379 [details] [diff] [review]
Style Inspector complete patch 10

Review of attachment 555379 [details] [diff] [review]:
-----------------------------------------------------------------

Yay. r+ assuming the browser.js change is trivial to fix.

::: browser/base/content/browser.js
@@ +193,5 @@
> +XPCOMUtils.defineLazyGetter(this, "CssLogic", function() {
> +  let tmp = {};
> +  Cu.import("resource:///modules/devtools/StyleInspector.jsm", tmp);
> +  return tmp.StyleInspector;
> +});

Shouldn't this be s/CssLogic/StyleInspector/? Especially since there's CssLogic.jsm that exports a |CssLogic|.
Comment 204 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-25 00:49:45 PDT
Created attachment 555671 [details] [diff] [review]
Style Inspector complete patch 11

(In reply to Justin Dolske [:Dolske] from comment #203)
> Comment on attachment 555379 [details] [diff] [review]
> Style Inspector complete patch 10
> 
> Review of attachment 555379 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> Yay. r+ assuming the browser.js change is trivial to fix.
> 
> ::: browser/base/content/browser.js
> @@ +193,5 @@
> > +XPCOMUtils.defineLazyGetter(this, "CssLogic", function() {
> > +  let tmp = {};
> > +  Cu.import("resource:///modules/devtools/StyleInspector.jsm", tmp);
> > +  return tmp.StyleInspector;
> > +});
> 
> Shouldn't this be s/CssLogic/StyleInspector/? Especially since there's
> CssLogic.jsm that exports a |CssLogic|.

lol ... I erased all of my changes to browser.js but forgot to qrefresh the changes. There was only this copy / paste error and a comment that were missing.

Thanks for your reviews.
Comment 205 Michael Ratcliffe [:miker] [:mratcliffe] 2011-08-29 06:30:48 PDT
Created attachment 556533 [details] [diff] [review]
[checked-in] 555671: Style Inspector complete patch 12

Rebased and ready to land pending try:
http://tbpl.allizom.org/?tree=Try&usebuildbot=1&rev=0f14101f86f1
Comment 206 Rob Campbell [:rc] (:robcee) 2011-08-30 09:47:18 PDT
Comment on attachment 556533 [details] [diff] [review]
[checked-in] 555671: Style Inspector complete patch 12

http://hg.mozilla.org/integration/fx-team/rev/26bc47cdea9e
Comment 207 Rob Campbell [:rc] (:robcee) 2011-08-31 05:32:23 PDT
Comment on attachment 556533 [details] [diff] [review]
[checked-in] 555671: Style Inspector complete patch 12

http://hg.mozilla.org/mozilla-central/rev/26bc47cdea9e
Comment 208 Francesco Lodolo [:flod] 2011-09-01 12:06:40 PDT
This string should use a single unicode … instead of ...
rule.showUnmatchedLink=One unmatched rule...;#1 unmatched rules...

Are you sure about these strings ("one" instead of #1)?
rule.showUnmatchedLink=One unmatched rule...;#1 unmatched rules...
property.numberOfUnmatchedRules=One unmatched rule;#1 unmatched rules

Here you used the number for both plural forms
property.numberOfRules=#1 rule;#1 rules

I still have to check a localized panel, but looking at the screenshot I predict a lot of space problems :-\
Comment 209 Girish Sharma [:Optimizer] 2011-09-03 23:35:15 PDT
This bug has been landed in Mozilla-Central , but I am unable to see any panel showing info on the style of the selected node. 
I am using Latest Nightly builds (x86). Win 7.
I also downloaded latest fx-team tinderbox build. Still unable to see any of the style inspector stuff. Just the usual "inspect" dev-tool which allows us to select a certain DOM node and see its HTML in the HTML viewer box below.
Comment 210 Michael Ratcliffe [:miker] [:mratcliffe] 2011-09-04 11:44:50 PDT
(In reply to scrapmachines from comment #209)
> This bug has been landed in Mozilla-Central , but I am unable to see any
> panel showing info on the style of the selected node. 
> I am using Latest Nightly builds (x86). Win 7.
> I also downloaded latest fx-team tinderbox build. Still unable to see any of
> the style inspector stuff. Just the usual "inspect" dev-tool which allows us
> to select a certain DOM node and see its HTML in the HTML viewer box below.

At the moment the style inspector is not linked with the highlighter. You can use it by opening the web console and typing e.g. inspectstyle(document.body)
Comment 211 Rimas Kudelis 2011-09-07 11:57:20 PDT
I wonder if the following strings defined in styleinspector.properties are still used:
rule.status.BEST=Best Match
rule.status.MATCHED=Matched
rule.status.PARENT_MATCH=Parent Match
rule.status.UNMATCHED=Unmatched

Also, Mozilla has been using ellipsis characters instead of three separate full stops for quite some time, but:
rule.showUnmatchedLink=One unmatched rule...;#1 unmatched rules...
Comment 212 Michael Ratcliffe [:miker] [:mratcliffe] 2011-09-08 01:44:34 PDT
(In reply to Rimas Kudelis from comment #211)
> I wonder if the following strings defined in styleinspector.properties are
> still used:
> rule.status.BEST=Best Match
> rule.status.MATCHED=Matched
> rule.status.PARENT_MATCH=Parent Match
> rule.status.UNMATCHED=Unmatched
> 

You are right, these strings are currently unused, but in the next few days I am adding a patch that displays a color legend and these strings will be used there.

> Also, Mozilla has been using ellipsis characters instead of three separate
> full stops for quite some time, but:
> rule.showUnmatchedLink=One unmatched rule...;#1 unmatched rules...

This is fixed in a future bug.

Note You need to log in before you can comment on or make changes to this bug.