Bug 763932 - [devtb] Add the right buttons to the Developer Toolbar; r=dao,jwalker,cedricv
authorPaul Rouget <paul@mozilla.com>
Mon, 25 Jun 2012 11:37:16 +0200
changeset 100385 d9e78d7def12e59a301fbe90faaba250ffd26781
parent 100384 b8956522f66ccadab4b3436c6f7ead70e550bbc9
child 100386 38eb0ceba74fd7f9613d8018d6493ae7dc084147
push idunknown
push userunknown
push dateunknown
reviewersdao, jwalker, cedricv
bugs763932
milestone16.0a1
Bug 763932 - [devtb] Add the right buttons to the Developer Toolbar; r=dao,jwalker,cedricv
browser/base/content/browser-appmenu.inc
browser/base/content/browser-menubar.inc
browser/base/content/browser-sets.inc
browser/base/content/browser.js
browser/base/content/browser.xul
browser/devtools/shared/test/browser_toolbar_basic.js
browser/devtools/styleeditor/StyleEditor.jsm
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -182,16 +182,17 @@
                     command="Tools:ChromeDebugger"/>
           <menuitem id="appmenu_scratchpad"
                     hidden="true"
                     label="&scratchpad.label;"
                     key="key_scratchpad"
                     command="Tools:Scratchpad"/>
           <menuitem id="appmenu_styleeditor"
                     hidden="true"
+                    type="checkbox"
                     label="&styleeditor.label;"
                     key="key_styleeditor"
                     command="Tools:StyleEditor"/>
           <menuitem id="appmenu_pageSource"
                     label="&viewPageSourceCmd.label;"
                     command="View:PageSource"
                     key="key_viewSource"/>
           <menuitem id="appmenu_errorConsole"
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -558,16 +558,17 @@
                             command="Tools:ChromeDebugger"/>
                   <menuitem id="menu_scratchpad"
                             hidden="true"
                             label="&scratchpad.label;"
                             accesskey="&scratchpad.accesskey;"
                             key="key_scratchpad"
                             command="Tools:Scratchpad"/>
                   <menuitem id="menu_styleeditor"
+                            type="checkbox"
                             hidden="true"
                             label="&styleeditor.label;"
                             accesskey="&styleeditor.accesskey;"
                             key="key_styleeditor"
                             command="Tools:StyleEditor"/>
                   <menuitem id="menu_pageSource"
                             accesskey="&pageSourceCmd.accesskey;"
                             label="&pageSourceCmd.label;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -90,17 +90,17 @@
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true"/>
     <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
     <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
     <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true"/>
     <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true"/>
     <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
-    <command id="Tools:StyleEditor" oncommand="StyleEditor.openChrome();" disabled="true"/>
+    <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true"/>
     <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1472,16 +1472,17 @@ var gBrowserInit = {
     // Enable Style Editor?
     let styleEditorEnabled = gPrefService.getBoolPref(StyleEditor.prefEnabledName);
     if (styleEditorEnabled) {
       document.getElementById("menu_styleeditor").hidden = false;
       document.getElementById("Tools:StyleEditor").removeAttribute("disabled");
 #ifdef MENUBAR_CAN_AUTOHIDE
       document.getElementById("appmenu_styleeditor").hidden = false;
 #endif
+      document.getElementById("developer-toolbar-styleeditor").hidden = false;
     }
 
 #ifdef MENUBAR_CAN_AUTOHIDE
     // If the user (or the locale) hasn't enabled the top-level "Character
     // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
     // hide it.
     if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
                                                Ci.nsIPrefLocalizedString).data)
@@ -1491,17 +1492,16 @@ var gBrowserInit = {
     // Enable Responsive UI?
     let responsiveUIEnabled = gPrefService.getBoolPref("devtools.responsiveUI.enabled");
     if (responsiveUIEnabled) {
       document.getElementById("menu_responsiveUI").hidden = false;
       document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
 #ifdef MENUBAR_CAN_AUTOHIDE
       document.getElementById("appmenu_responsiveUI").hidden = false;
 #endif
-      document.getElementById("developer-toolbar-responsiveui").hidden = false;
     }
 
     let appMenuButton = document.getElementById("appmenu-button");
     let appMenuPopup = document.getElementById("appmenu-popup");
     if (appMenuButton && appMenuPopup) {
       let appMenuOpening = null;
       appMenuButton.addEventListener("mousedown", function(event) {
         if (event.button == 0)
@@ -7365,50 +7365,39 @@ var StyleEditor = {
    * Opens the style editor. If the UI is already open, it will be focused.
    *
    * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
    * @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
    * @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
    */
   openChrome: function SE_openChrome(aSelectedStyleSheet, aLine, aCol)
   {
-    const CHROME_URL = "chrome://browser/content/styleeditor.xul";
-    const CHROME_WINDOW_TYPE = "Tools:StyleEditor";
-    const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
-
-    // focus currently open Style Editor window for this document, if any
     let contentWindow = gBrowser.selectedBrowser.contentWindow;
-    let contentWindowID = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-      getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
-    let enumerator = Services.wm.getEnumerator(CHROME_WINDOW_TYPE);
-    while (enumerator.hasMoreElements()) {
-      var win = enumerator.getNext();
-      if (win.styleEditorChrome.contentWindowID == contentWindowID) {
-        if (aSelectedStyleSheet) {
-          win.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol);
-        }
-        win.focus();
-        return win;
-      }
-    }
-
-    let args = {
-      contentWindow: contentWindow,
-      selectedStyleSheet: aSelectedStyleSheet,
-      line: aLine,
-      col: aCol
-    };
-    args.wrappedJSObject = args;
-    let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
-                                              CHROME_WINDOW_FLAGS, args);
-    chromeWindow.focus();
-    return chromeWindow;
+    let win = this.StyleEditorManager.getEditorForWindow(contentWindow);
+    if (win) {
+      this.StyleEditorManager.selectEditor(win);
+      return win;
+    } else {
+      return this.StyleEditorManager.newEditor(contentWindow,
+                                               aSelectedStyleSheet, aLine, aCol);
+    }
+  },
+
+  toggle: function SE_toggle()
+  {
+    this.StyleEditorManager.toggleEditor(gBrowser.contentWindow);
   }
 };
 
+XPCOMUtils.defineLazyGetter(StyleEditor, "StyleEditorManager", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/StyleEditor.jsm", tmp);
+  return new tmp.StyleEditorManager(window);
+});
+
 
 XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
 #ifdef XP_WIN
   // Only show resizers on Windows 2000 and XP
   let sysInfo = Components.classes["@mozilla.org/system-info;1"]
                           .getService(Components.interfaces.nsIPropertyBag2);
   return parseFloat(sysInfo.getProperty("version")) < 6;
 #else
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1040,21 +1040,21 @@
                          label="&webConsoleButton.label;"
                          class="devtools-toolbarbutton"
                          command="Tools:WebConsole"/>
           <toolbarbutton id="developer-toolbar-inspector"
                          label="&inspectorButton.label;"
                          class="devtools-toolbarbutton"
                          hidden="true"
                          command="Tools:Inspect"/>
-          <toolbarbutton id="developer-toolbar-responsiveui"
-                         label="&responsiveDesignTool.label;"
+          <toolbarbutton id="developer-toolbar-styleeditor"
+                         label="&styleeditor.label;"
                          class="devtools-toolbarbutton"
                          hidden="true"
-                         command="Tools:ResponsiveUI"/>
+                         command="Tools:StyleEditor"/>
           <toolbarbutton id="developer-toolbar-debugger"
                          label="&debuggerMenu.label2;"
                          class="devtools-toolbarbutton"
                          hidden="true"
                          command="Tools:Debugger"/>
 #ifndef XP_MACOSX
           <toolbarbutton id="developer-toolbar-closebutton"
                          class="devtools-closebutton"
--- a/browser/devtools/shared/test/browser_toolbar_basic.js
+++ b/browser/devtools/shared/test/browser_toolbar_basic.js
@@ -30,65 +30,100 @@ function isChecked(b) {
 }
 
 function checkOpen() {
   ok(DeveloperToolbar.visible, "DeveloperToolbar is visible in checkOpen");
 
   let close = document.getElementById("developer-toolbar-closebutton");
   let webconsole = document.getElementById("developer-toolbar-webconsole");
   let inspector = document.getElementById("developer-toolbar-inspector");
+  let styleeditor = document.getElementById("developer-toolbar-styleeditor");
   let debuggr = document.getElementById("developer-toolbar-debugger");
 
   ok(close, "Close button exists");
 
   ok(!isChecked(webconsole), "web console button state 1");
   ok(!isChecked(inspector), "inspector button state 1");
   ok(!isChecked(debuggr), "debugger button state 1");
+  ok(!isChecked(styleeditor), "styleeditor button state 1");
 
   document.getElementById("Tools:WebConsole").doCommand();
 
   ok(isChecked(webconsole), "web console button state 2");
   ok(!isChecked(inspector), "inspector button state 2");
   ok(!isChecked(debuggr), "debugger button state 2");
+  ok(!isChecked(styleeditor), "styleeditor button state 2");
 
   document.getElementById("Tools:Inspect").doCommand();
 
   ok(isChecked(webconsole), "web console button state 3");
   ok(isChecked(inspector), "inspector button state 3");
   ok(!isChecked(debuggr), "debugger button state 3");
+  ok(!isChecked(styleeditor), "styleeditor button state 3");
 
   // Christmas tree!
 
   // The web console opens synchronously, but closes asynchronously.
   let hud = imported.HUDService.getHudByWindow(content);
   imported.HUDService.disableAnimation(hud.hudId);
 
   document.getElementById("Tools:WebConsole").doCommand();
 
   ok(!isChecked(webconsole), "web console button state 6");
   ok(isChecked(inspector), "inspector button state 6");
   ok(!isChecked(debuggr), "debugger button state 6");
+  ok(!isChecked(styleeditor), "styleeditor button state 6");
 
   document.getElementById("Tools:Inspect").doCommand();
 
   ok(!isChecked(webconsole), "web console button state 7");
   ok(!isChecked(inspector), "inspector button state 7");
   ok(!isChecked(debuggr), "debugger button state 7");
+  ok(!isChecked(styleeditor), "styleeditor button state 7");
 
   // All closed
 
   // Check we can open and close and retain button state
   document.getElementById("Tools:Inspect").doCommand();
 
   ok(!isChecked(webconsole), "web console button state 8");
   ok(isChecked(inspector), "inspector button state 8");
   ok(!isChecked(debuggr), "debugger button state 8");
+  ok(!isChecked(styleeditor), "styleeditor button state 8");
 
-  oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkClosed));
-  document.getElementById("Tools:DevToolbar").doCommand();
+
+  // Test Style Editor
+  document.getElementById("Tools:StyleEditor").doCommand();
+
+  ok(!isChecked(webconsole), "web console button state 9");
+  ok(isChecked(inspector), "inspector button state 9");
+  ok(!isChecked(debuggr), "debugger button state 9");
+  ok(isChecked(styleeditor), "styleeditor button state 9");
+
+  // Test Debugger
+  document.getElementById("Tools:Debugger").doCommand();
+
+  ok(!isChecked(webconsole), "web console button state 9");
+  ok(isChecked(inspector), "inspector button state 9");
+  ok(isChecked(debuggr), "debugger button state 9");
+  ok(isChecked(styleeditor), "styleeditor button state 9");
+
+  addTab("about:blank", function(browser, tab) {
+    info("Opening a new tab");
+
+    ok(!isChecked(webconsole), "web console button state 10");
+    ok(!isChecked(inspector), "inspector button state 10");
+    ok(!isChecked(debuggr), "debugger button state 10");
+    ok(!isChecked(styleeditor), "styleeditor button state 10");
+
+    gBrowser.removeCurrentTab();
+
+    oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkClosed));
+    document.getElementById("Tools:DevToolbar").doCommand();
+  });
 }
 
 function checkClosed() {
   ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in checkClosed");
 
   // Check we grok state even when closed
   document.getElementById("Tools:WebConsole").doCommand();
 
@@ -97,20 +132,25 @@ function checkClosed() {
 }
 
 function checkReOpen() {
   ok(DeveloperToolbar.visible, "DeveloperToolbar is visible in checkReOpen");
 
   let webconsole = document.getElementById("developer-toolbar-webconsole");
   let inspector = document.getElementById("developer-toolbar-inspector");
   let debuggr = document.getElementById("developer-toolbar-debugger");
+  let styleeditor = document.getElementById("developer-toolbar-styleeditor");
 
-  ok(isChecked(webconsole), "web console button state 9");
-  ok(isChecked(inspector), "inspector button state 9");
-  ok(!isChecked(debuggr), "debugger button state 9");
+  ok(isChecked(webconsole), "web console button state 99");
+  ok(isChecked(inspector), "inspector button state 99");
+  ok(isChecked(debuggr), "debugger button state 99");
+  ok(isChecked(styleeditor), "styleeditor button state 99");
+
+  // We close the style editor (not automatically closed)
+  document.getElementById("Tools:StyleEditor").doCommand();
 
   oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkReClosed));
   document.getElementById("developer-toolbar-closebutton").doCommand();
 }
 
 function checkReClosed() {
   ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in checkReClosed");
 
--- a/browser/devtools/styleeditor/StyleEditor.jsm
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -1,16 +1,16 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const EXPORTED_SYMBOLS = ["StyleEditor", "StyleEditorFlags"];
+const EXPORTED_SYMBOLS = ["StyleEditor", "StyleEditorFlags", "StyleEditorManager"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
                    .getService(Ci.inIDOMUtils);
 
@@ -1155,8 +1155,138 @@ function setupBracketCompletion(aSourceE
                   getInterface(Ci.nsIDOMWindowUtils);
     let handled = utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
     utils.sendKeyEvent("keypress", 0, charCode, modifiers, !handled);
     utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
     // and rewind caret
     aSourceEditor.setCaretOffset(aSourceEditor.getCaretOffset() - 1);
   }, false);
 }
+
+/**
+  * Manage the different editors instances.
+  */
+
+function StyleEditorManager(aWindow) {
+  this.chromeWindow = aWindow;
+  this.listenToTabs();
+  this.editors = new WeakMap();
+}
+
+StyleEditorManager.prototype = {
+
+  /**
+   * Get the editor for a specific content window.
+   */
+  getEditorForWindow: function SEM_getEditorForWindow(aContentWindow) {
+    return this.editors.get(aContentWindow);
+  },
+
+  /**
+   * Focus the editor and select a stylesheet.
+   *
+   * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
+   * @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
+   * @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
+   */
+  selectEditor: function SEM_selectEditor(aWindow, aSelectedStyleSheet, aLine, aCol) {
+    if (aSelectedStyleSheet) {
+      aWindow.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol);
+    }
+    aWindow.focus();
+  },
+
+  /**
+   * Open a new editor.
+   *
+   * @param {Window} content window.
+   * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
+   * @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
+   * @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
+   */
+  newEditor: function SEM_newEditor(aContentWindow, aSelectedStyleSheet, aLine, aCol) {
+    const CHROME_URL = "chrome://browser/content/styleeditor.xul";
+    const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
+
+    let args = {
+      contentWindow: aContentWindow,
+      selectedStyleSheet: aSelectedStyleSheet,
+      line: aLine,
+      col: aCol
+    };
+    args.wrappedJSObject = args;
+    let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
+                                              CHROME_WINDOW_FLAGS, args);
+
+    chromeWindow.onunload = function() {
+      if (chromeWindow.location == CHROME_URL) {
+        // not about:blank being unloaded
+        this.unregisterEditor(aContentWindow);
+      }
+    }.bind(this);
+    chromeWindow.focus();
+
+    this.editors.set(aContentWindow, chromeWindow);
+
+    this.refreshCommand();
+
+    return chromeWindow;
+  },
+
+  /**
+   * Toggle an editor.
+   *
+   * @param {Window} associated content window.
+   */
+  toggleEditor: function SEM_toggleEditor(aContentWindow) {
+    let editor = this.getEditorForWindow(aContentWindow);
+    if (editor) {
+      editor.close();
+    } else {
+      this.newEditor(aContentWindow);
+    }
+  },
+
+  /**
+   * Close an editor.
+   *
+   * @param {Window} associated content window.
+   */
+  unregisterEditor: function SEM_unregisterEditor(aContentWindow) {
+    let chromeWindow = this.editors.get(aContentWindow);
+    if (chromeWindow) {
+      chromeWindow.close();
+    }
+    this.editors.delete(aContentWindow);
+    this.refreshCommand();
+  },
+
+  /**
+   * Update the status of tool's menuitems and buttons.
+   */
+  refreshCommand: function SEM_refreshCommand() {
+    let contentWindow = this.chromeWindow.gBrowser.contentWindow;
+    let command = this.chromeWindow.document.getElementById("Tools:StyleEditor");
+
+    let win = this.getEditorForWindow(contentWindow);
+    if (win) {
+      command.setAttribute("checked", "true");
+    } else {
+      command.setAttribute("checked", "false");
+    }
+  },
+
+  /**
+   * Trigger refreshCommand when needed.
+   */
+  listenToTabs: function SEM_listenToTabs() {
+    let win = this.chromeWindow;
+    let tabs = win.gBrowser.tabContainer;
+
+    let bound_refreshCommand = this.refreshCommand.bind(this);
+    tabs.addEventListener("TabSelect", bound_refreshCommand, true);
+
+    win.addEventListener("unload", function onClose(aEvent) {
+      tabs.removeEventListener("TabSelect", bound_refreshCommand, true);
+      win.removeEventListener("unload", onClose, false);
+    }, false);
+  },
+}