Bug 1180584 - Support multiple badges on the Hamburger menu button. r=markh
authorEdouard Oger <edouard.oger@gmail.com>
Thu, 06 Aug 2015 14:47:32 +1000
changeset 256580 8d243e99ae6e68a1d40f463f04eac4c309c4a128
parent 256579 0c90be998ea07d0f2e05b93e4ac5b6742f4dc2c0
child 256581 4df929a2069c682561080347b51a4f177ff8c29f
push id29180
push userkwierso@gmail.com
push dateFri, 07 Aug 2015 00:23:32 +0000
treeherdermozilla-central@b735f4eea935 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh
bugs1180584
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1180584 - Support multiple badges on the Hamburger menu button. r=markh
browser/base/content/browser-fxaccounts.js
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_menuButtonBadgeManager.js
browser/themes/linux/jar.mn
browser/themes/osx/jar.mn
browser/themes/shared/customizableui/panelUIOverlay.inc.css
browser/themes/shared/update-badge-failed.svg
browser/themes/windows/jar.mn
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -272,33 +272,40 @@ let gFxAccounts = {
       }
 
       // Reset the button to its original state.
       this.panelUILabel.setAttribute("label", defaultLabel);
       this.panelUIStatus.removeAttribute("tooltiptext");
       this.panelUIFooter.removeAttribute("fxastatus");
       this.panelUIFooter.removeAttribute("fxaprofileimage");
       this.panelUIAvatar.style.removeProperty("list-style-image");
+      let showErrorBadge = false;
 
       if (!this._inCustomizationMode && userData) {
         // At this point we consider the user as logged-in (but still can be in an error state)
         if (this.loginFailed) {
           let tooltipDescription = this.strings.formatStringFromName("reconnectDescription", [userData.email], 1);
           this.panelUIFooter.setAttribute("fxastatus", "error");
           this.panelUILabel.setAttribute("label", errorLabel);
           this.panelUIStatus.setAttribute("tooltiptext", tooltipDescription);
+          showErrorBadge = true;
         } else {
           this.panelUIFooter.setAttribute("fxastatus", "signedin");
           this.panelUILabel.setAttribute("label", userData.email);
           this.panelUIStatus.setAttribute("tooltiptext", signedInTooltiptext);
         }
         if (profileInfoEnabled) {
           this.panelUIFooter.setAttribute("fxaprofileimage", "enabled");
         }
       }
+      if (showErrorBadge) {
+        gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
+      } else {
+        gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
+      }
     }
 
     let updateWithProfile = (profile) => {
       if (!this._inCustomizationMode && profileInfoEnabled) {
         if (profile.displayName) {
           this.panelUILabel.setAttribute("label", profile.displayName);
         }
         if (profile.avatar) {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2564,45 +2564,84 @@ function SetPageProxyState(aState)
 }
 
 function PageProxyClickHandler(aEvent)
 {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
     middleMousePaste(aEvent);
 }
 
+let gMenuButtonBadgeManager = {
+  BADGEID_APPUPDATE: "update",
+  BADGEID_FXA: "fxa",
+
+  fxaBadge: null,
+  appUpdateBadge: null,
+
+  _showBadge: function () {
+    let badgeToShow = this.appUpdateBadge || this.fxaBadge;
+
+    if (badgeToShow) {
+      PanelUI.menuButton.setAttribute("badge-status", badgeToShow);
+    } else {
+      PanelUI.menuButton.removeAttribute("badge-status");
+    }
+  },
+
+  _changeBadge: function (badgeId, badgeStatus = null) {
+    if (badgeId == this.BADGEID_APPUPDATE) {
+      this.appUpdateBadge = badgeStatus;
+    } else if (badgeId == this.BADGEID_FXA) {
+      this.fxaBadge = badgeStatus;
+    } else {
+      Cu.reportError("This badge ID is unknown!");
+    }
+    this._showBadge();
+  },
+
+  addBadge: function (badgeId, badgeStatus) {
+    if (!badgeStatus) {
+      Cu.reportError("badgeStatus must be defined");
+      return;
+    }
+    this._changeBadge(badgeId, badgeStatus);
+  },
+
+  removeBadge: function (badgeId) {
+    this._changeBadge(badgeId);
+  }
+};
+
 // Setup the hamburger button badges for updates, if enabled.
 let gMenuButtonUpdateBadge = {
   enabled: false,
   badgeWaitTime: 0,
   timer: null,
 
   init: function () {
     try {
       this.enabled = Services.prefs.getBoolPref("app.update.badge");
     } catch (e) {}
     if (this.enabled) {
       try {
         this.badgeWaitTime = Services.prefs.getIntPref("app.update.badgeWaitTime");
       } catch (e) {
         this.badgeWaitTime = 345600; // 4 days
       }
-      PanelUI.menuButton.classList.add("badged-button");
       Services.obs.addObserver(this, "update-staged", false);
       Services.obs.addObserver(this, "update-downloaded", false);
     }
   },
 
   uninit: function () {
     if (this.timer)
       this.timer.cancel();
     if (this.enabled) {
       Services.obs.removeObserver(this, "update-staged");
       Services.obs.removeObserver(this, "update-downloaded");
-      PanelUI.panel.removeEventListener("popupshowing", this, true);
       this.enabled = false;
     }
   },
 
   onMenuPanelCommand: function(event) {
     if (event.originalTarget.getAttribute("update-status") === "succeeded") {
       // restart the app
       let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
@@ -2640,20 +2679,18 @@ let gMenuButtonUpdateBadge = {
     // to non-staged updates, add a badge to the hamburger menu to indicate an
     // update will be applied once the browser restarts.
     this.displayBadge(true);
     this.uninit();
   },
 
   displayBadge: function (succeeded) {
     let status = succeeded ? "succeeded" : "failed";
-    PanelUI.menuButton.setAttribute("update-status", status);
-    if (!succeeded) {
-      PanelUI.menuButton.setAttribute("badge", "!");
-    }
+    let badgeStatus = "update-" + status;
+    gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, badgeStatus);
 
     let stringId;
     let updateButtonText;
     if (succeeded) {
       let brandBundle = document.getElementById("bundle_brand");
       let brandShortName = brandBundle.getString("brandShortName");
       stringId = "appmenu.restartNeeded.description";
       updateButtonText = gNavigatorBundle.getFormattedString(stringId,
@@ -2662,25 +2699,16 @@ let gMenuButtonUpdateBadge = {
       stringId = "appmenu.updateFailed.description";
       updateButtonText = gNavigatorBundle.getString(stringId);
     }
 
     let updateButton = document.getElementById("PanelUI-update-status");
     updateButton.setAttribute("label", updateButtonText);
     updateButton.setAttribute("update-status", status);
     updateButton.hidden = false;
-
-    PanelUI.panel.addEventListener("popupshowing", this, true);
-  },
-
-  handleEvent: function(e) {
-    if (e.type === "popupshowing") {
-      PanelUI.menuButton.removeAttribute("badge");
-      PanelUI.panel.removeEventListener("popupshowing", this, true);
-    }
   }
 };
 
 // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_CHECKED   = 2;
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_UNCHECKED = 3;
 const TLS_ERROR_REPORT_TELEMETRY_MANUAL_SEND    = 4;
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_SEND      = 5;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -955,17 +955,17 @@
                      class="toolbarbutton-1 chromeclass-toolbar-additional overflow-button"
                      skipintoolbarset="true"
                      tooltiptext="&navbarOverflow.label;"/>
 
       <toolbaritem id="PanelUI-button"
                    class="chromeclass-toolbar-additional"
                    removable="false">
         <toolbarbutton id="PanelUI-menu-button"
-                       class="toolbarbutton-1"
+                       class="toolbarbutton-1 badged-button"
                        consumeanchor="PanelUI-button"
                        label="&brandShortName;"
                        tooltiptext="&appmenu.tooltip;"/>
       </toolbaritem>
 
       <hbox id="window-controls" hidden="true" pack="end" skipintoolbarset="true"
             ordinal="1000">
         <toolbarbutton id="minimize-button"
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -504,8 +504,9 @@ support-files =
   readerModeArticle.html
 [browser_readerMode_hidden_nodes.js]
 support-files =
   readerModeArticleHiddenNodes.html
 [browser_bug1124271_readerModePinnedTab.js]
 support-files =
   readerModeArticle.html
 [browser_domFullscreen_fullscreenMode.js]
+[browser_menuButtonBadgeManager.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_menuButtonBadgeManager.js
@@ -0,0 +1,28 @@
+/* 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/. */
+
+let menuButton = document.getElementById("PanelUI-menu-button");
+
+add_task(function* testButtonActivities() {
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
+  is(menuButton.hasAttribute("badge"), false, "Should not have the badge attribute set");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
+  is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-succeeded");
+  is(menuButton.getAttribute("badge-status"), "update-succeeded", "Should have update-succeeded badge status (update > fxa)");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-failed");
+  is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
+
+  gMenuButtonBadgeManager.addBadge("unknownbadge", "attr");
+  is(menuButton.getAttribute("badge-status"), "update-failed", "Should not have changed badge status");
+
+  gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE);
+  is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
+
+  gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
+});
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -100,16 +100,17 @@ browser.jar:
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/theme-switcher-icon.png              (../shared/theme-switcher-icon.png)
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/update-badge.svg                        (../shared/update-badge.svg)
+  skin/classic/browser/update-badge-failed.svg                 (../shared/update-badge-failed.svg)
   skin/classic/browser/urlbar-arrow.png                     (../shared/urlbar-arrow.png)
   skin/classic/browser/urlbar-arrow@2x.png                  (../shared/urlbar-arrow@2x.png)
   skin/classic/browser/session-restore.svg                  (../shared/incontent-icons/session-restore.svg)
   skin/classic/browser/tab-crashed.svg                      (../shared/incontent-icons/tab-crashed.svg)
   skin/classic/browser/welcome-back.svg                     (../shared/incontent-icons/welcome-back.svg)
   skin/classic/browser/reader-tour.png                      (../shared/reader/reader-tour.png)
   skin/classic/browser/reader-tour@2x.png                   (../shared/reader/reader-tour@2x.png)
   skin/classic/browser/readerMode.svg                       (../shared/reader/readerMode.svg)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -133,16 +133,17 @@ browser.jar:
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar@2x.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-inverted@2x.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
   skin/classic/browser/update-badge.svg                        (../shared/update-badge.svg)
+  skin/classic/browser/update-badge-failed.svg                 (../shared/update-badge-failed.svg)
   skin/classic/browser/urlbar-history-dropmarker.png
   skin/classic/browser/urlbar-history-dropmarker@2x.png
   skin/classic/browser/urlbar-arrow.png                        (../shared/urlbar-arrow.png)
   skin/classic/browser/urlbar-arrow@2x.png                     (../shared/urlbar-arrow@2x.png)
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/urlbar-popup-blocked@2x.png
   skin/classic/browser/session-restore.svg            (../shared/incontent-icons/session-restore.svg)
   skin/classic/browser/tab-crashed.svg                (../shared/incontent-icons/tab-crashed.svg)
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -96,23 +96,43 @@
   border-image: linear-gradient(transparent, rgba(0,0,0,.1) 20%, rgba(0,0,0,.1) 80%, transparent);
   border-image-slice: 1;
 }
 
 #nav-bar[brighttext] > #PanelUI-button {
   border-image-source: linear-gradient(transparent, rgba(100%,100%,100%,.2) 20%, rgba(100%,100%,100%,.2) 80%, transparent);
 }
 
-#PanelUI-menu-button[update-status="succeeded"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
+#PanelUI-menu-button[badge-status] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: -moz-box;
+  height: 10px;
+  width: 10px;
+  background-size: contain;
+  border: none;
+}
+
+#PanelUI-menu-button[badge-status="update-succeeded"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   background-image: url(chrome://browser/skin/update-badge.svg);
   background-color: #74BF43;
-  height: 10px;
-  width: 10px;
-  border: none;
+}
+
+#PanelUI-menu-button[badge-status="update-failed"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
+  background-image: url(chrome://browser/skin/update-badge-failed.svg);
+  background-color: #D90000;
+}
+
+#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
+  background-color: transparent;
+  background-image: url(chrome://browser/skin/warning.svg);
+  box-shadow: none;
+  filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
+}
+
+#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
+  filter: none;
 }
 
 .panel-subviews {
   padding: 4px;
   background-clip: padding-box;
   border-left: 1px solid hsla(210,4%,10%,.3);
   box-shadow: 0 3px 5px hsla(210,4%,10%,.1),
               0 0 7px hsla(210,4%,10%,.1);
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/update-badge-failed.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="10px" height="10px">
+  <path d="M5,6C4.2,6,3.5,6.7,3.5,7.5S4.2,9,5,9s1.5-0.7,1.5-1.5S5.8,6,5,6z M5,5L5,5c0.6,0,1-0.4,1-1l0.2-2.8 C6.2,0.5,5.7,0,5,0S3.8,0.5,3.8,1.2L4,4C4,4.6,4.4,5,5,5z" fill="#fff"/>
+</svg>
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -138,16 +138,17 @@ browser.jar:
         skin/classic/browser/Toolbar-win8@2x.png
         skin/classic/browser/Toolbar-XP.png
         skin/classic/browser/toolbarbutton-dropdown-arrow.png
         skin/classic/browser/toolbarbutton-dropdown-arrow-XPVista7.png
         skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png
         skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
         skin/classic/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
         skin/classic/browser/update-badge.svg                        (../shared/update-badge.svg)
+        skin/classic/browser/update-badge-failed.svg                 (../shared/update-badge-failed.svg)
         skin/classic/browser/urlbar-arrow.png                        (../shared/urlbar-arrow.png)
         skin/classic/browser/urlbar-arrow@2x.png                     (../shared/urlbar-arrow@2x.png)
         skin/classic/browser/urlbar-popup-blocked.png
         skin/classic/browser/urlbar-history-dropmarker.png
         skin/classic/browser/urlbar-history-dropmarker@2x.png
         skin/classic/browser/urlbar-history-dropmarker-preWin10.png
         skin/classic/browser/urlbar-history-dropmarker-preWin10@2x.png
         skin/classic/browser/session-restore.svg                     (../shared/incontent-icons/session-restore.svg)