Bug 971626 - [Australis] Restore Defaults should collapse Title Bar. r=Gijs
authorJared Wein <jwein@mozilla.com>
Fri, 14 Feb 2014 13:51:33 -0500
changeset 169371 96946c57e083c2f5b49de40a7748d38d250780ab
parent 169223 0beafa155ee98ec67da3a35896fd2fdd629a70b6
child 169372 91176f7dd8e212a0c1fa60edd04f03ca2a1e4d6b
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersGijs
bugs971626
milestone30.0a1
Bug 971626 - [Australis] Restore Defaults should collapse Title Bar. r=Gijs
browser/app/profile/firefox.js
browser/components/customizableui/content/customizeMode.inc.xul
browser/components/customizableui/src/CustomizableUI.jsm
browser/components/customizableui/src/CustomizeMode.jsm
browser/components/customizableui/test/browser_970511_undo_restore_default.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1355,16 +1355,19 @@ pref("geo.wifi.uri", "https://www.google
 
 // Necko IPC security checks only needed for app isolation for cookies/cache/etc:
 // currently irrelevant for desktop e10s
 pref("network.disable.ipc.security", true);
 
 // CustomizableUI debug logging.
 pref("browser.uiCustomization.debug", false);
 
+// CustomizableUI state of the browser's user interface
+pref("browser.uiCustomization.state", "");
+
 // The URL where remote content that composes the UI for Firefox Accounts should
 // be fetched. Must use HTTPS.
 pref("identity.fxaccounts.remote.uri", "https://accounts.firefox.com/?service=sync&context=fx_desktop_v1");
 
 // The URL where remote content that forces re-authentication for Firefox Accounts
 // should be fetched.  Must use HTTPS.
 pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v1");
 
--- a/browser/components/customizableui/content/customizeMode.inc.xul
+++ b/browser/components/customizableui/content/customizeMode.inc.xul
@@ -25,17 +25,17 @@
 #NB: because oncommand fires after click, by the time we've fired, the checkbox binding
 #    will already have switched the button's state, so this is correct:
               oncommand="gCustomizeMode.toggleTitlebar(this.hasAttribute('checked'))"/>
 #endif
       <button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars;" class="customizationmode-button" type="menu">
         <menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
       </button>
       <spacer flex="1"/>
-      <button id="customization-undo-reset"
+      <button id="customization-undo-reset-button"
               class="customizationmode-button"
               hidden="true"
               oncommand="gCustomizeMode.undoReset();"
               label="&undoCmd.label;"/>
       <button id="customization-reset-button" oncommand="gCustomizeMode.reset();" label="&customizeMode.restoreDefaults;" class="customizationmode-button"/>
     </hbox>
   </vbox>
   <vbox id="customization-panel-container">
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -31,16 +31,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const kSpecialWidgetPfx = "customizableui-special-";
 
 const kPrefCustomizationState        = "browser.uiCustomization.state";
 const kPrefCustomizationAutoAdd      = "browser.uiCustomization.autoAdd";
 const kPrefCustomizationDebug        = "browser.uiCustomization.debug";
+const kPrefDrawInTitlebar            = "browser.tabs.drawInTitlebar";
 
 /**
  * The keys are the handlers that are fired when the event type (the value)
  * is fired on the subview. A widget that provides a subview has the option
  * of providing onViewShowing and onViewHiding event handlers.
  */
 const kSubviewEvents = [
   "ViewShowing",
@@ -123,17 +124,20 @@ let gBuildAreas = new Map();
  */
 let gBuildWindows = new Map();
 
 let gNewElementCount = 0;
 let gGroupWrapperCache = new Map();
 let gSingleWrapperCache = new WeakMap();
 let gListeners = new Set();
 
-let gUIStateBeforeReset = null;
+let gUIStateBeforeReset = {
+  uiCustomizationState: null,
+  drawInTitlebar: null,
+};
 
 let gModuleName = "[CustomizableUI]";
 #include logging.js
 
 let CustomizableUIInternal = {
   initialize: function() {
     LOG("Initializing");
 
@@ -692,17 +696,17 @@ let CustomizableUIInternal = {
 
     this.registerBuildArea(CustomizableUI.AREA_PANEL, aPanelContents);
   },
 
   onWidgetAdded: function(aWidgetId, aArea, aPosition) {
     this.insertNode(aWidgetId, aArea, aPosition, true);
 
     if (!gResetting) {
-      gUIStateBeforeReset = null;
+      this._clearPreviousUIState();
     }
   },
 
   onWidgetRemoved: function(aWidgetId, aArea) {
     let areaNodes = gBuildAreas.get(aArea);
     if (!areaNodes) {
       return;
     }
@@ -751,29 +755,29 @@ let CustomizableUIInternal = {
       }
 
       let windowCache = gSingleWrapperCache.get(window);
       if (windowCache) {
         windowCache.delete(aWidgetId);
       }
     }
     if (!gResetting) {
-      gUIStateBeforeReset = null;
+      this._clearPreviousUIState();
     }
   },
 
   onWidgetMoved: function(aWidgetId, aArea, aOldPosition, aNewPosition) {
     this.insertNode(aWidgetId, aArea, aNewPosition);
     if (!gResetting) {
-      gUIStateBeforeReset = null;
+      this._clearPreviousUIState();
     }
   },
 
   onCustomizeEnd: function(aWindow) {
-    gUIStateBeforeReset = null;
+    this._clearPreviousUIState();
   },
 
   registerBuildArea: function(aArea, aNode) {
     // We ensure that the window is registered to have its customization data
     // cleaned up when unloading.
     let window = aNode.ownerDocument.defaultView;
     if (window.closed) {
       return;
@@ -2071,20 +2075,22 @@ let CustomizableUIInternal = {
     // was reset above.
     this._rebuildRegisteredAreas();
 
     gResetting = false;
   },
 
   _resetUIState: function() {
     try {
-      gUIStateBeforeReset = Services.prefs.getCharPref(kPrefCustomizationState);
+      gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref(kPrefDrawInTitlebar);
+      gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
     } catch(e) { }
 
     Services.prefs.clearUserPref(kPrefCustomizationState);
+    Services.prefs.clearUserPref(kPrefDrawInTitlebar);
     LOG("State reset");
 
     // Reset placements to make restoring default placements possible.
     gPlacements = new Map();
     gDirtyAreaCache = new Set();
     gSeenWidgets = new Set();
     // Clear the saved state to ensure that defaults will be used.
     gSavedState = null;
@@ -2109,27 +2115,41 @@ let CustomizableUIInternal = {
       }
     }
   },
 
   /**
    * Undoes a previous reset, restoring the state of the UI to the state prior to the reset.
    */
   undoReset: function() {
-    if (!gUIStateBeforeReset) {
+    if (gUIStateBeforeReset.uiCustomizationState == null ||
+        gUIStateBeforeReset.drawInTitlebar == null) {
       return;
     }
-    Services.prefs.setCharPref(kPrefCustomizationState, gUIStateBeforeReset);
+    let uiCustomizationState = gUIStateBeforeReset.uiCustomizationState;
+    let drawInTitlebar = gUIStateBeforeReset.drawInTitlebar;
+
+    // Need to clear the previous state before setting the prefs
+    // because pref observers may check if there is a previous UI state.
+    this._clearPreviousUIState();
+
+    Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState);
+    Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar);
     this.loadSavedState();
     for (let areaId of Object.keys(gSavedState.placements)) {
       let placements = gSavedState.placements[areaId];
       gPlacements.set(areaId, placements);
     }
     this._rebuildRegisteredAreas();
-    gUIStateBeforeReset = null;
+  },
+
+  _clearPreviousUIState: function() {
+    Object.getOwnPropertyNames(gUIStateBeforeReset).forEach((prop) => {
+      gUIStateBeforeReset[prop] = null;
+    });
   },
 
   /**
    * @param {String|Node} aWidget - widget ID or a widget node (preferred for performance).
    * @return {Boolean} whether the widget is removable
    */
   isWidgetRemovable: function(aWidget) {
     let widgetId;
@@ -2266,16 +2286,21 @@ let CustomizableUIInternal = {
         if (currentPlacements[i] != defaultPlacements[i]) {
           LOG("Found " + currentPlacements[i] + " in " + areaId + " where " +
               defaultPlacements[i] + " was expected!");
           return false;
         }
       }
     }
 
+    if (Services.prefs.prefHasUserValue(kPrefDrawInTitlebar)) {
+      LOG(kPrefDrawInTitlebar + " pref is non-default");
+      return false;
+    }
+
     return true;
   }
 };
 Object.freeze(CustomizableUIInternal);
 
 this.CustomizableUI = {
   /**
    * Constant reference to the ID of the menu panel.
@@ -2889,17 +2914,18 @@ this.CustomizableUI = {
 
   /**
    * Can the last Restore Defaults operation be undone.
    *
    * @return A boolean stating whether an undo of the
    *         Restore Defaults can be performed.
    */
   get canUndoReset() {
-    return !!gUIStateBeforeReset;
+    return gUIStateBeforeReset.uiCustomizationState != null ||
+           gUIStateBeforeReset.drawInTitlebar != null;
   },
 
   /**
    * Get the placement of a widget. This is by far the best way to obtain
    * information about what the state of your widget is. The internals of
    * this call are cheap (no DOM necessary) and you will know where the user
    * has put your widget.
    *
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -994,18 +994,18 @@ CustomizeMode.prototype = {
   },
 
   _updateResetButton: function() {
     let btn = this.document.getElementById("customization-reset-button");
     btn.disabled = CustomizableUI.inDefaultState;
   },
 
   _updateUndoResetButton: function() {
-    let undoReset =  this.document.getElementById("customization-undo-reset");
-    undoReset.hidden = !CustomizableUI.canUndoReset;
+    let undoResetButton =  this.document.getElementById("customization-undo-reset-button");
+    undoResetButton.hidden = !CustomizableUI.canUndoReset;
   },
 
   handleEvent: function(aEvent) {
     switch(aEvent.type) {
       case "toolbarvisibilitychange":
         this._onToolbarVisibilityChange(aEvent);
         break;
       case "dragstart":
@@ -1047,17 +1047,19 @@ CustomizeMode.prototype = {
 #endif
     }
   },
 
 #ifdef CAN_DRAW_IN_TITLEBAR
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "nsPref:changed":
+        this._updateResetButton();
         this._updateTitlebarButton();
+        this._updateUndoResetButton();
         break;
     }
   },
 
   _updateTitlebarButton: function() {
     let drawInTitlebar = true;
     try {
       drawInTitlebar = Services.prefs.getBoolPref(kDrawInTitlebarPref);
--- a/browser/components/customizableui/test/browser_970511_undo_restore_default.js
+++ b/browser/components/customizableui/test/browser_970511_undo_restore_default.js
@@ -6,60 +6,102 @@
 
 // Restoring default should show an "undo" option which undoes the restoring operation.
 add_task(function() {
   let homeButtonId = "home-button";
   CustomizableUI.removeWidgetFromArea(homeButtonId);
   yield startCustomizing();
   ok(!CustomizableUI.inDefaultState, "Not in default state to begin with");
   is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
-  let undoReset = document.getElementById("customization-undo-reset");
-  is(undoReset.hidden, true, "The undo button is hidden before reset");
+  let undoResetButton = document.getElementById("customization-undo-reset-button");
+  is(undoResetButton.hidden, true, "The undo button is hidden before reset");
 
   yield gCustomizeMode.reset();
 
   ok(CustomizableUI.inDefaultState, "In default state after reset");
-  is(undoReset.hidden, false, "The undo button is visible after reset");
+  is(undoResetButton.hidden, false, "The undo button is visible after reset");
 
-  undoReset.click();
+  undoResetButton.click();
   yield waitForCondition(function() !gCustomizeMode.resetting);
   ok(!CustomizableUI.inDefaultState, "Not in default state after reset-undo");
-  is(undoReset.hidden, true, "The undo button is hidden after clicking on the undo button");
+  is(undoResetButton.hidden, true, "The undo button is hidden after clicking on the undo button");
   is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
 
   yield gCustomizeMode.reset();
 });
 
 // Performing an action after a reset will hide the reset button.
 add_task(function() {
   let homeButtonId = "home-button";
   CustomizableUI.removeWidgetFromArea(homeButtonId);
   ok(!CustomizableUI.inDefaultState, "Not in default state to begin with");
   is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
-  let undoReset = document.getElementById("customization-undo-reset");
-  is(undoReset.hidden, true, "The undo button is hidden before reset");
+  let undoResetButton = document.getElementById("customization-undo-reset-button");
+  is(undoResetButton.hidden, true, "The undo button is hidden before reset");
 
   yield gCustomizeMode.reset();
 
   ok(CustomizableUI.inDefaultState, "In default state after reset");
-  is(undoReset.hidden, false, "The undo button is visible after reset");
+  is(undoResetButton.hidden, false, "The undo button is visible after reset");
 
   CustomizableUI.addWidgetToArea(homeButtonId, CustomizableUI.AREA_PANEL);
-  is(undoReset.hidden, true, "The undo button is hidden after another change");
+  is(undoResetButton.hidden, true, "The undo button is hidden after another change");
 });
 
 // "Restore defaults", exiting customize, and re-entering shouldn't show the Undo button
 add_task(function() {
-  let undoReset = document.getElementById("customization-undo-reset");
-  is(undoReset.hidden, true, "The undo button is hidden before a reset");
+  let undoResetButton = document.getElementById("customization-undo-reset-button");
+  is(undoResetButton.hidden, true, "The undo button is hidden before a reset");
   ok(!CustomizableUI.inDefaultState, "The browser should not be in default state");
   yield gCustomizeMode.reset();
 
-  is(undoReset.hidden, false, "The undo button is hidden after a reset");
+  is(undoResetButton.hidden, false, "The undo button is visible after a reset");
   yield endCustomizing();
   yield startCustomizing();
-  is(undoReset.hidden, true, "The undo reset button should be hidden after entering customization mode");
+  is(undoResetButton.hidden, true, "The undo reset button should be hidden after entering customization mode");
+});
+
+// Bug 971626 - Restore Defaults should collapse the Title Bar
+add_task(function() {
+  if (Services.appinfo.OS != "WINNT" &&
+      Services.appinfo.OS != "Darwin") {
+    return;
+  }
+  let prefName = "browser.tabs.drawInTitlebar";
+  let defaultValue = Services.prefs.getBoolPref(prefName);
+  let restoreDefaultsButton = document.getElementById("customization-reset-button");
+  let titleBarButton = document.getElementById("customization-titlebar-visibility-button");
+  let undoResetButton = document.getElementById("customization-undo-reset-button");
+  ok(CustomizableUI.inDefaultState, "Should be in default state at start of test");
+  ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled when in default state");
+  is(titleBarButton.hasAttribute("checked"), !defaultValue, "Title bar button should reflect pref value");
+  is(undoResetButton.hidden, true, "Undo reset button should be hidden at start of test");
+
+  Services.prefs.setBoolPref(prefName, !defaultValue);
+  ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled when pref changed");
+  is(titleBarButton.hasAttribute("checked"), defaultValue, "Title bar button should reflect changed pref value");
+  ok(!CustomizableUI.inDefaultState, "With titlebar flipped, no longer default");
+  is(undoResetButton.hidden, true, "Undo reset button should be hidden after pref change");
+
+  yield gCustomizeMode.reset();
+  ok(restoreDefaultsButton.disabled, "Restore defaults button should be disabled after reset");
+  is(titleBarButton.hasAttribute("checked"), !defaultValue, "Title bar button should reflect default value after reset");
+  is(Services.prefs.getBoolPref(prefName), defaultValue, "Reset should reset drawInTitlebar");
+  ok(CustomizableUI.inDefaultState, "In default state after titlebar reset");
+  is(undoResetButton.hidden, false, "Undo reset button should be visible after reset");
+  ok(!undoResetButton.disabled, "Undo reset button should be enabled after reset");
+
+  yield gCustomizeMode.undoReset();
+  ok(!restoreDefaultsButton.disabled, "Restore defaults button should be enabled after undo-reset");
+  is(titleBarButton.hasAttribute("checked"), defaultValue, "Title bar button should reflect undo-reset value");
+  ok(!CustomizableUI.inDefaultState, "No longer in default state after undo");
+  is(Services.prefs.getBoolPref(prefName), !defaultValue, "Undo-reset goes back to previous pref value");
+  is(undoResetButton.hidden, true, "Undo reset button should be hidden after undo-reset clicked");
+
+  Services.prefs.clearUserPref(prefName);
+  ok(CustomizableUI.inDefaultState, "In default state after pref cleared");
+  is(undoResetButton.hidden, true, "Undo reset button should be hidden at end of test");
 });
 
 add_task(function asyncCleanup() {
   yield gCustomizeMode.reset();
   yield endCustomizing();
 });