Bug 663831 - Style inspector should be controllable from the highlighter; r=msucan,gavin
☠☠ backed out by c75401a8160a ☠ ☠
authorRob Campbell <rcampbell@mozilla.com>
Fri, 23 Sep 2011 13:50:33 -0300
changeset 77398 f1409901573ab504ffa35030198084679c0d8154
parent 77397 ce51db254f18be7172e30dfd9bd79ea199143862
child 77399 121518f3df44e8837ef64e7536717a8fdeb36006
child 77447 c75401a8160a4a92b38c8c9f79bf7be854779712
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersmsucan, gavin
bugs663831
milestone9.0a1
Bug 663831 - Style inspector should be controllable from the highlighter; r=msucan,gavin
browser/devtools/highlighter/inspector.js
browser/devtools/styleinspector/CssHtmlTree.jsm
browser/devtools/styleinspector/StyleInspector.jsm
browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js
browser/devtools/webconsole/HUDService.jsm
browser/locales/en-US/chrome/browser/styleinspector.properties
--- a/browser/devtools/highlighter/inspector.js
+++ b/browser/devtools/highlighter/inspector.js
@@ -203,17 +203,16 @@ Highlighter.prototype = {
     closeButton.id = "highlighter-close-button";
     closeButton.appendChild(document.createElement("image"));
 
     closeButton.setAttribute("onclick", "InspectorUI.closeInspectorUI(false);");
 
     aParent.appendChild(closeButton);
   },
 
-
   /**
    * Destroy the nodes.
    */
   destroy: function Highlighter_destroy()
   {
     this.browser.removeEventListener("scroll", this, true);
     this.browser.removeEventListener("resize", this, true);
     this._highlightRect = null;
@@ -835,16 +834,36 @@ var InspectorUI = {
     gBrowser.addProgressListener(InspectorProgressListener);
   },
 
   /**
    * Register and initialize any included tools.
    */
   initTools: function IUI_initTools()
   {
+    // Style inspector
+    if (Services.prefs.getBoolPref("devtools.styleinspector.enabled") &&
+        !this.toolRegistered("styleinspector")) {
+      let stylePanel = this.StyleInspector.createPanel(true);
+      this.registerTool({
+        id: "styleinspector",
+        label: InspectorUI.StyleInspector.l10n("style.highlighter.button.label"),
+        tooltiptext: InspectorUI.StyleInspector.l10n("style.highlighter.button.tooltip"),
+        accesskey: InspectorUI.StyleInspector.l10n("style.highlighter.accesskey"),
+        context: stylePanel,
+        get isOpen() stylePanel.isOpen(),
+        onSelect: stylePanel.selectNode,
+        show: stylePanel.showTool,
+        hide: stylePanel.hideTool,
+        dim: stylePanel.dimTool,
+        panel: stylePanel,
+        unregister: stylePanel.destroy,
+      });
+      this.stylePanel = stylePanel;
+    }
   },
 
   /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
     this.highlighter = new Highlighter(this.browser);
@@ -961,32 +980,34 @@ var InspectorUI = {
       this.removeEventListener("popuphidden", treePanelHidden, false);
 
       InspectorUI.closing = false;
       Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null);
     }, false);
 
     this.treePanel.hidePopup();
     delete this.treePanel;
+    delete this.stylePanel;
   },
 
   /**
    * Begin inspecting webpage, attach page event listeners, activate
    * highlighter event listeners.
    */
   startInspecting: function IUI_startInspecting()
   {
     // if currently editing an attribute value, starting
     // "live inspection" mode closes the editor
     if (this.editingContext)
       this.closeEditor();
 
     document.getElementById("inspector-inspect-toolbutton").checked = true;
     this.attachPageListeners();
     this.inspecting = true;
+    this.toolsDim(true);
     this.highlighter.veilContainer.removeAttribute("locked");
   },
 
   /**
    * Stop inspecting webpage, detach page listeners, disable highlighter
    * event listeners.
    * @param aPreventScroll
    *        Prevent scroll in the HTML tree?
@@ -995,16 +1016,17 @@ var InspectorUI = {
   {
     if (!this.inspecting) {
       return;
     }
 
     document.getElementById("inspector-inspect-toolbutton").checked = false;
     this.detachPageListeners();
     this.inspecting = false;
+    this.toolsDim(false);
     if (this.highlighter.node) {
       this.select(this.highlighter.node, true, true, !aPreventScroll);
     } else {
       this.select(null, true, true);
     }
     this.highlighter.veilContainer.setAttribute("locked", true);
   },
 
@@ -1154,20 +1176,22 @@ var InspectorUI = {
       node = this.getRepObject(aEvent.target);
     }
 
     if (node) {
       if (hitTwisty) {
         this.ioBox.toggleObject(node);
       } else {
         if (this.inspecting) {
+          this.toolsSelect();
           this.stopInspecting(true);
         } else {
           this.select(node, true, false);
           this.highlighter.highlightNode(node);
+          this.toolsSelect();
         }
       }
     }
   },
 
   /**
    * Handle double-click events in the html tree panel.
    * (double-clicking an attribute value allows it to be edited)
@@ -1626,16 +1650,17 @@ var InspectorUI = {
    *   label: "Button label",
    *   icon: "chrome://somepath.png",
    *   tooltiptext: "Button tooltip",
    *   accesskey: "S",
    *   isOpen: object.property, (getter) returning true if tool is open.
    *   onSelect: object.method,
    *   show: object.method, called to show the tool when button is pressed.
    *   hide: object.method, called to hide the tool when button is pressed.
+   *   dim: object.method, called to disable a tool during highlighting.
    *   unregister: object.method, called when tool should be destroyed.
    *   panel: myTool.panel
    * }
    *
    * @param aRegObj Object
    *        The Registration Object used to register this tool described
    *        above. The tool should cache this object for later deregistration.
    */
@@ -1789,16 +1814,29 @@ var InspectorUI = {
     this.toolsDo(function IUI_toolsOnSelect(aTool) {
       if (aTool.isOpen) {
         aTool.onSelect.call(aTool.context, InspectorUI.selection);
       }
     });
   },
 
   /**
+   * Dim or undim each tool in the tools collection
+   * @param aState true = dim, false = undim
+   */
+  toolsDim: function IUI_toolsDim(aState)
+  {
+    this.toolsDo(function IUI_toolsOnSelect(aTool) {
+      if (aTool.isOpen && "dim" in aTool) {
+        aTool.dim.call(aTool.context, aState);
+      }
+    });
+  },
+
+  /**
    * Loop through all registered tools and pass each into the provided function
    * @param aFunction The function to which each tool is to be passed
    */
   toolsDo: function IUI_toolsDo(aFunction)
   {
     for each (let tool in this.tools) {
       aFunction(tool);
     }
@@ -2055,8 +2093,14 @@ XPCOMUtils.defineLazyGetter(InspectorUI,
   return document.getElementById("Tools:Inspect");
 });
 
 XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
   return Services.strings.
          createBundle("chrome://browser/locale/inspector.properties");
 });
 
+XPCOMUtils.defineLazyGetter(InspectorUI, "StyleInspector", function () {
+  var obj = {};
+  Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj);
+  return obj.StyleInspector;
+});
+
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm
+++ b/browser/devtools/styleinspector/CssHtmlTree.jsm
@@ -16,19 +16,20 @@
  * 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):
- *   Joe Walker (jwalker@mozilla.com) (original author)
+ *   Joe Walker (jwalker@mozilla.com) (Original Author)
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *   Michael Ratcliffe <mratcliffe@mozilla.com>
+ *   Rob Campbell <rcampbell@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
--- a/browser/devtools/styleinspector/StyleInspector.jsm
+++ b/browser/devtools/styleinspector/StyleInspector.jsm
@@ -16,17 +16,18 @@
  * 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) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Mike Ratcliffe <mratcliffe@mozilla.com>
+ *   Mike Ratcliffe <mratcliffe@mozilla.com> (Original Author)
+ *   Rob Campbell <rcampbell@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -104,31 +105,33 @@ var StyleInspector = {
     popupSet.appendChild(panel);
 
     /**
      * Iframe's onload event
      */
     let iframeReady = false;
     function SI_iframeOnload() {
       iframe.removeEventListener("load", SI_iframeOnload, true);
-      panel.cssLogic = new CssLogic();
-      panel.cssHtmlTree = new CssHtmlTree(iframe, panel.cssLogic, panel);
       iframeReady = true;
       if (panelReady) {
         SI_popupShown.call(panel);
       }
     }
 
     /**
      * Initialize the popup when it is first shown
      */
     let panelReady = false;
     function SI_popupShown() {
       panelReady = true;
       if (iframeReady) {
+        if (!this.cssLogic) {
+          this.cssLogic = new CssLogic();
+          this.cssHtmlTree = new CssHtmlTree(iframe, this.cssLogic, this);
+        }
         let selectedNode = this.selectedNode || null;
         this.cssLogic.highlight(selectedNode);
         this.cssHtmlTree.highlight(selectedNode);
         Services.obs.notifyObservers(null, "StyleInspector-opened", null);
       }
     }
 
     /**
@@ -157,39 +160,71 @@ var StyleInspector = {
     /**
      * Select a node to inspect in the Style Inspector panel
      *
      * @param aNode The node to inspect
      */
     panel.selectNode = function SI_selectNode(aNode)
     {
       this.selectedNode = aNode;
-      if (this.isOpen()) {
+      if (this.isOpen() && !this.hasAttribute("dimmed")) {
         this.cssLogic.highlight(aNode);
         this.cssHtmlTree.highlight(aNode);
-      } else {
-        let win = Services.wm.getMostRecentWindow("navigator:browser");
-        this.openPopup(win.gBrowser.selectedBrowser, "end_before", 0, 0, false, false);
       }
     };
 
     /**
      * Destroy the style panel, remove listeners etc.
      */
     panel.destroy = function SI_destroy()
     {
+      if (!this.cssLogic)
+        return;
+      if (this.isOpen())
+        this.hideTool();
       this.cssLogic = null;
       this.cssHtmlTree = null;
       this.removeEventListener("popupshown", SI_popupShown);
       this.removeEventListener("popuphidden", SI_popupHidden);
       this.parentNode.removeChild(this);
       Services.obs.notifyObservers(null, "StyleInspector-closed", null);
     };
 
     /**
+     * Dim or undim a panel by setting or removing a dimmed attribute.
+     *
+     * @param aState
+     *        true = dim, false = undim
+     */
+    panel.dimTool = function SI_dimTool(aState)
+    {
+      if (!this.isOpen())
+        return;
+
+      if (aState) {
+        this.setAttribute("dimmed", "true");
+      } else if (this.hasAttribute("dimmed")) {
+        this.removeAttribute("dimmed");
+      }
+    };
+
+    panel.showTool = function SI_showTool(aSelection)
+    {
+      this.selectNode(aSelection);
+      let win = Services.wm.getMostRecentWindow("navigator:browser");
+      this.openPopup(win.gBrowser.selectedBrowser, "end_before", 0, 0,
+        false, false);
+    };
+
+    panel.hideTool = function SI_hideTool()
+    {
+      this.hidePopup();
+    };
+
+    /**
      * Is the Style Inspector initialized?
      * @returns {Boolean} true or false
      */
     function isInitialized()
     {
       return panel.cssLogic && panel.cssHtmlTree;
     }
 
--- a/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js
+++ b/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js
@@ -169,31 +169,23 @@ function styleInspectorClosedByHide()
                            "StyleInspector-closed", false);
   closeConsole();
 }
 
 function styleInspectorClosedFromConsole1()
 {
   Services.obs.removeObserver(styleInspectorClosedFromConsole1,
                               "StyleInspector-closed", false);
-  info("Style Inspector 1 closed");
-  Services.obs.addObserver(styleInspectorClosedFromConsole2,
-                           "StyleInspector-closed", false);
-}
-
-function styleInspectorClosedFromConsole2()
-{
-  Services.obs.removeObserver(styleInspectorClosedFromConsole2,
-                              "StyleInspector-closed", false);
-  info("Style Inspector 2 closed");
+  info("Style Inspector 1 and 2 closed");
   executeSoon(cleanUp);
 }
 
 function cleanUp()
 {
-  let popupSet = document.getElementById("mainPopupSet");
-  ok(!popupSet.lastChild.hasAttribute("hudToolId"),
+  let panels = document.querySelector("panel[hudToolId]");
+  ok(!panels,
      "all style inspector panels are now detached and ready for garbage collection");
 
   info("cleaning up");
+
   doc = hudBox = stylePanels = jsterm = null;
   finishTest();
 }
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -4433,17 +4433,17 @@ function JSTermHelper(aJSTerm)
       errstr = HUDService.getStr("inspectStyle.mustBeDomNode");
     } else if (!(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration)) {
       errstr = HUDService.getStr("inspectStyle.nodeHasNoStyleProps");
     }
 
     if (!errstr) {
       let stylePanel = StyleInspector.createPanel();
       stylePanel.setAttribute("hudToolId", aJSTerm.hudId);
-      stylePanel.selectNode(aNode);
+      stylePanel.showTool(aNode);
     } else {
       aJSTerm.writeOutput(errstr + "\n", CATEGORY_OUTPUT, SEVERITY_ERROR);
     }
   };
 
   /**
    * Prints aObject to the output.
    *
--- a/browser/locales/en-US/chrome/browser/styleinspector.properties
+++ b/browser/locales/en-US/chrome/browser/styleinspector.properties
@@ -36,8 +36,14 @@ rule.sourceElement=element
 # these are the category names.
 group.Text_Fonts_and_Color=Text, Fonts & Color
 group.Background=Background
 group.Dimensions=Dimensions
 group.Positioning_and_Page_Flow=Positioning and Page Flow
 group.Borders=Borders
 group.Lists=Lists
 group.Effects_and_Other=Effects and Other
+
+# LOCALIZATION NOTE (style.highlighter.button): These strings are used inside
+# html tree of the highlighter for the style inspector button
+style.highlighter.button.label=Style
+style.highlighter.accesskey=S
+style.highlighter.button.tooltip=Inspect element styles