Bug 1270006 - Fill portion of the icon on download button as indicator to download progress r=paolo draft
authorRex Lee <rexboy@mozilla.com>
Wed, 24 Aug 2016 18:39:53 +0800
changeset 489056 b32eef7cb98ad9a99debbc1d8617b9789348a5d9
parent 480952 f505911eb333d5ae8c2bf5c44f7b85add6450b53
child 489069 20134cff4a5ccf9a1e290392cd3aafdb527a18c4
child 491736 8a95ceda191ac9e807045efa59940bf5271ef4b4
push id46739
push userbmo:rexboy@mozilla.com
push dateFri, 24 Feb 2017 09:33:55 +0000
reviewerspaolo
bugs1270006
milestone54.0a1
Bug 1270006 - Fill portion of the icon on download button as indicator to download progress r=paolo MozReview-Commit-ID: BqYow8jWCVD
browser/base/content/browser.css
browser/components/downloads/DownloadsCommon.jsm
browser/components/downloads/content/indicator.js
browser/components/downloads/content/indicatorOverlay.xul
browser/themes/linux/downloads/indicator.css
browser/themes/osx/downloads/indicator.css
browser/themes/shared/downloads/indicator.inc.css
browser/themes/windows/downloads/indicator.css
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -958,20 +958,24 @@ notification[value="translation"] {
 toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > .toolbarbutton-badge-stack > image.toolbarbutton-icon {
   display: -moz-box;
 }
 
 toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-indicator-anchor {
   display: none;
 }
 
-#downloads-button:-moz-any([progress], [counter], [paused]) #downloads-indicator-icon,
-#downloads-button:not(:-moz-any([progress], [counter], [paused]))
-                                                   #downloads-indicator-progress-area
-{
+#downloads-button.withProgressBar:-moz-any([progress], [counter], [paused]) #downloads-indicator-icon,
+#downloads-button:not(:-moz-any([progress], [counter], [paused])) #downloads-indicator-progress-area {
+  visibility: hidden;
+}
+
+/* Hide elements for another type of progressmeter if it's not in use. */
+#downloads-button.withProgressBar #downloads-indicator-progress-icon,
+#downloads-button:not(.withProgressBar) #downloads-indicator-progress-area {
   visibility: hidden;
 }
 
 /* Combobox dropdown renderer */
 #ContentSelectDropdown > menupopup {
   /* The menupopup itself should always be rendered LTR to ensure the scrollbar aligns with
    * the dropdown arrow on the dropdown widget. If a menuitem is RTL, its style will be set accordingly */
   direction: ltr;
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -142,16 +142,23 @@ PrefObserver.register({
  */
 this.DownloadsCommon = {
   ATTENTION_NONE: "",
   ATTENTION_SUCCESS: "success",
   ATTENTION_WARNING: "warning",
   ATTENTION_SEVERE: "severe",
 
   /**
+   * This can be used by add-on experiments as a killswitch for the new style
+   * progress indication. This will be removed in bug 1329109 after the new
+   * indicator is released.
+   **/
+  arrowStyledIndicator: true,
+
+  /**
    * Returns an object whose keys are the string names from the downloads string
    * bundle, and whose values are either the translated strings or functions
    * returning formatted strings.
    */
   get strings() {
     let strings = {};
     let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
     let enumerator = sb.getSimpleEnumeration();
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -212,20 +212,31 @@ const DownloadsIndicatorView = {
    * Prepares the downloads indicator to be displayed.
    */
   ensureInitialized() {
     if (this._initialized) {
       return;
     }
     this._initialized = true;
 
+    this._setIndicatorType();
     window.addEventListener("unload", this.onWindowUnload);
     DownloadsCommon.getIndicatorData(window).addView(this);
   },
 
+  _setIndicatorType() {
+    // We keep a killerswitch for old-styled progressbar for now. Corresponding
+    // css class is added here to reflect the type chosen for showing progress.
+    let node = CustomizableUI.getWidget("downloads-button")
+                             .forWindow(window).node;
+
+    node.classList.toggle("withProgressBar",
+                          !DownloadsCommon.arrowStyledIndicator);
+  },
+
   /**
    * Frees the internal resources related to the indicator.
    */
   ensureTerminated() {
     if (!this._initialized) {
       return;
     }
     this._initialized = false;
@@ -406,31 +417,50 @@ const DownloadsIndicatorView = {
       // XBL binding isn't applied if the element is invisible for any reason.
       this._indicatorCounter.setAttribute("value", aValue);
     }
     return aValue;
   },
   _counter: null,
 
   /**
-   * Progress indication to display, from 0 to 100, or -1 if unknown.  The
-   * progress bar is hidden if the current progress is unknown and no status
-   * text is set in the "counter" property.
+   * Progress indication to display, from 0 to 100, or -1 if unknown.
+   * Bar-type:
+   *   The progress bar is hidden if the current progress is unknown and no
+   *   status text is set in the "counter" property.
+   * Arrow-type:
+   *   progress is not visible if the current progress is unknown.
    */
   set percentComplete(aValue) {
+    // For arrow type only:
+    // We show portion of the success icon in propotional with the download
+    // progress as an indicator. the PROGRESS_ICON_EMPTY_HEIGHT_PERCENT and
+    // PROGRESS_ICON_FULL_HEIGHT_PERCENT correspond to how much portion of the
+    // icon should be displayed in 0% and 100%.
+    const PROGRESS_ICON_EMPTY_HEIGHT_PERCENT = 35;
+    const PROGRESS_ICON_FULL_HEIGHT_PERCENT = 87;
+
     if (!this._operational) {
       return this._percentComplete;
     }
 
     if (this._percentComplete !== aValue) {
       this._percentComplete = aValue;
-      if (this._percentComplete >= 0)
+      this._refreshAttention();
+
+      if (this._percentComplete >= 0) {
         this.indicator.setAttribute("progress", "true");
-      else
+        this._progressIcon.style.height = (this._percentComplete *
+          (PROGRESS_ICON_FULL_HEIGHT_PERCENT -
+           PROGRESS_ICON_EMPTY_HEIGHT_PERCENT) / 100 +
+           PROGRESS_ICON_EMPTY_HEIGHT_PERCENT) + "%";
+      } else {
         this.indicator.removeAttribute("progress");
+        this._progressIcon.style.height = "0";
+      }
       // We have to set the attribute instead of using the property because the
       // XBL binding isn't applied if the element is invisible for any reason.
       this._indicatorProgress.setAttribute("value", Math.max(aValue, 0));
     }
     return aValue;
   },
   _percentComplete: null,
 
@@ -458,39 +488,49 @@ const DownloadsIndicatorView = {
 
   /**
    * Set when the indicator should draw user attention to itself.
    */
   set attention(aValue) {
     if (!this._operational) {
       return this._attention;
     }
-
     if (this._attention != aValue) {
       this._attention = aValue;
+      this._refreshAttention();
+    }
+    return this._attention;
+  },
 
-      // Check if the downloads button is in the menu panel, to determine which
-      // button needs to get a badge.
-      let widgetGroup = CustomizableUI.getWidget("downloads-button");
-      let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
+  _refreshAttention() {
+    // Check if the downloads button is in the menu panel, to determine which
+    // button needs to get a badge.
+    let widgetGroup = CustomizableUI.getWidget("downloads-button");
+    let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
 
-      if (aValue == DownloadsCommon.ATTENTION_NONE) {
-        this.indicator.removeAttribute("attention");
-        if (inMenu) {
-          gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
-        }
-      } else {
-        this.indicator.setAttribute("attention", aValue);
-        if (inMenu) {
-          let badgeClass = "download-" + aValue;
-          gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD, badgeClass);
-        }
+    // For arrow-Styled indicator, suppress success attention if we have
+    // progress in toolbar
+    let suppressAttention = DownloadsCommon.arrowStyledIndicator && !inMenu &&
+      this._attention == DownloadsCommon.ATTENTION_SUCCESS &&
+      this._percentComplete >= 0;
+
+    if (suppressAttention || this._attention == DownloadsCommon.ATTENTION_NONE) {
+      this.indicator.removeAttribute("attention");
+      if (inMenu) {
+        gMenuButtonBadgeManager.removeBadge(
+                                      gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
+      }
+    } else {
+      this.indicator.setAttribute("attention", this._attention);
+      if (inMenu) {
+        let badgeClass = "download-" + this._attention;
+        gMenuButtonBadgeManager.addBadge(
+                          gMenuButtonBadgeManager.BADGEID_DOWNLOAD, badgeClass);
       }
     }
-    return aValue;
   },
   _attention: DownloadsCommon.ATTENTION_NONE,
 
   //////////////////////////////////////////////////////////////////////////////
   //// User interface event functions
 
   onWindowUnload() {
     // This function is registered as an event listener, we can't use "this".
@@ -534,16 +574,17 @@ const DownloadsIndicatorView = {
     if (handled) {
       aEvent.preventDefault();
     }
   },
 
   _indicator: null,
   __indicatorCounter: null,
   __indicatorProgress: null,
+  __progressIcon: null,
 
   /**
    * Returns a reference to the main indicator element, or null if the element
    * is not present in the browser window yet.
    */
   get indicator() {
     if (this._indicator) {
       return this._indicator;
@@ -571,23 +612,29 @@ const DownloadsIndicatorView = {
       (this.__indicatorCounter = document.getElementById("downloads-indicator-counter"));
   },
 
   get _indicatorProgress() {
     return this.__indicatorProgress ||
       (this.__indicatorProgress = document.getElementById("downloads-indicator-progress"));
   },
 
+  get _progressIcon() {
+    return this.__progressIcon ||
+      (this.__progressIcon = document.getElementById("downloads-indicator-progress-icon"));
+  },
+
   get notifier() {
     return this._notifier ||
       (this._notifier = document.getElementById("downloads-notification-anchor"));
   },
 
   _onCustomizedAway() {
     this._indicator = null;
+    this.__progressIcon = null;
     this.__indicatorCounter = null;
     this.__indicatorProgress = null;
   },
 
   afterCustomize() {
     // If the cached indicator is not the one currently in the document,
     // invalidate our references
     if (this._indicator != document.getElementById("downloads-button")) {
--- a/browser/components/downloads/content/indicatorOverlay.xul
+++ b/browser/components/downloads/content/indicatorOverlay.xul
@@ -20,17 +20,19 @@
        downloads-button. -->
   <toolbarbutton id="downloads-button" indicator="true">
     <!-- The panel's anchor area is smaller than the outer button, but must
          always be visible and must not move or resize when the indicator
          state changes, otherwise the panel could change its position or lose
          its arrow unexpectedly. -->
     <stack id="downloads-indicator-anchor"
            consumeanchor="downloads-button">
+      <vbox id="downloads-indicator-icon">
+        <vbox id="downloads-indicator-progress-icon"/>
+      </vbox>
       <vbox id="downloads-indicator-progress-area" pack="center">
         <description id="downloads-indicator-counter"/>
         <progressmeter id="downloads-indicator-progress" class="plain"
                        min="0" max="100"/>
       </vbox>
-      <vbox id="downloads-indicator-icon"/>
     </stack>
   </toolbarbutton>
 </overlay>
--- a/browser/themes/linux/downloads/indicator.css
+++ b/browser/themes/linux/downloads/indicator.css
@@ -1,12 +1,14 @@
 /* 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/. */
 
+%include ../../shared/downloads/indicator.inc.css
+
 /*** Status and progress indicator ***/
 
 #downloads-animation-container {
   min-height: 1px;
   min-width: 1px;
   height: 1px;
   margin-bottom: -1px;
   /* Makes the outermost animation container element positioned, so that its
@@ -16,41 +18,41 @@
   /* The selected tab may overlap #downloads-indicator-notification */
   z-index: 5;
 }
 
 /*** Main indicator icon ***/
 
 @media not all and (min-resolution: 1.1dppx) {
   #downloads-button {
-    --downloads-indicator-icon: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
-    --downloads-indicator-icon-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
-    --downloads-indicator-icon-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
-    --downloads-indicator-icon-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
   }
 }
 
 @media (min-resolution: 1.1dppx) {
   #downloads-button {
-    --downloads-indicator-icon: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
-    --downloads-indicator-icon-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 36, 396, 72, 360);
-    --downloads-indicator-icon-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
-    --downloads-indicator-icon-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 36, 396, 72, 360);
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 36, 396, 72, 360);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 36, 396, 72, 360);
   }
 }
 
 #downloads-button[cui-areatype="toolbar"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background: var(--downloads-indicator-icon) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   width: 18px;
   height: 18px;
   background-size: 18px;
 }
 
 toolbar[brighttext] #downloads-button[cui-areatype="toolbar"]:not([attention="success"]) > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-inverted);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: -moz-box;
   height: 8px;
   width: 8px;
   min-width: 0;
@@ -74,46 +76,46 @@ toolbar[brighttext] #downloads-button[cu
 }
 
 #downloads-button[cui-areatype="toolbar"][attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
 #downloads-button[cui-areatype="toolbar"][attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
   filter: none;
 }
 
 #downloads-button[cui-areatype="toolbar"][attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-attention);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button[cui-areatype="toolbar"][attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-attention-inverted);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 #downloads-button[cui-areatype="menu-panel"][attention="success"] {
   list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel.png");
   -moz-image-region: auto;
 }
 
 /* In the next few rules, we use :not([counter]) as a shortcut that is
    equivalent to -moz-any([progress], [paused]). */
 
 #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background: var(--downloads-indicator-icon) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   background-size: 12px;
 }
 
 toolbar[brighttext] #downloads-button:not([counter]):not([attention="success"]) > #downloads-indicator-anchor > #downloads-button-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-inverted);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-attention);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-attention-inverted);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 /*** Download notifications ***/
 
 #downloads-indicator-notification {
   opacity: 0;
   background-size: 16px;
   background-position: center;
--- a/browser/themes/osx/downloads/indicator.css
+++ b/browser/themes/osx/downloads/indicator.css
@@ -1,12 +1,14 @@
 /* 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/. */
 
+%include ../../shared/downloads/indicator.inc.css
+
 /*** Status and progress indicator ***/
 
 #downloads-indicator-anchor {
   min-width: 18px;
   min-height: 18px;
 }
 
 #downloads-animation-container {
@@ -19,23 +21,39 @@
      This is required by the animated event notification. */
   position: relative;
   /* The selected tab may overlap #downloads-indicator-notification */
   z-index: 5;
 }
 
 /*** Main indicator icon ***/
 
+#downloads-button {
+  --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
+  --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 36, 198, 54, 180);
+  --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+  --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 36, 198, 54, 180);
+}
+
+@media (min-resolution: 2dppx) {
+  #downloads-button {
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 72, 396, 108, 360);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 72, 396, 108, 360);
+  }
+}
+
 #downloads-indicator-icon {
-  background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
-                              0, 198, 18, 180) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
+  background-size: 18px;
 }
 
 toolbar[brighttext] #downloads-indicator-icon {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: -moz-box;
   height: 8px;
   width: 8px;
   min-width: 0;
@@ -59,100 +77,67 @@ toolbar[brighttext] #downloads-indicator
 }
 
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
   filter: none;
 }
 
 #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 36, 198, 54, 180);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 36, 198, 54, 180);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 #downloads-button[cui-areatype="menu-panel"][attention="success"] {
   list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel.png");
   -moz-image-region: auto;
 }
 
 /* In the next few rules, we use :not([counter]) as a shortcut that is
    equivalent to -moz-any([progress], [paused]). */
 
 #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
-                              0, 198, 18, 180) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   background-size: 12px;
 }
 
 toolbar[brighttext] #downloads-button:not([counter]):not([attention="success"]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 36, 198, 54, 180);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 36, 198, 54, 180);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 @media (min-resolution: 2dppx) {
-  #downloads-indicator-icon {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
-    background-size: 18px;
-  }
-
-  toolbar[brighttext] #downloads-indicator-icon {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
-  }
-
-  #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
-  }
-
-  toolbar[brighttext] #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"),
-                                      0, 396, 36, 360);
-  }
-
-  #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 72, 396, 108, 360);
-  }
-
-  toolbar[brighttext] #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 72, 396, 108, 360);
-  }
-
   #downloads-button[cui-areatype="menu-panel"][attention="success"] {
     list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel@2x.png");
   }
-
-  #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 72, 396, 108, 360);
-  }
-
-  toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-    background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 72, 396, 108, 360);
-  }
 }
 
 /*** Download notifications ***/
 
 #downloads-indicator-notification {
   opacity: 0;
   background-size: 16px;
   background-position: center;
   background-repeat: no-repeat;
   width: 16px;
   height: 16px;
 }
 
+/*** Progress bar and text ***/
+
 @keyframes downloadsIndicatorNotificationStartRight {
   from { opacity: 0; transform: translate(-128px, 128px) scale(8); }
   20%  { opacity: .85; animation-timing-function: ease-out; }
   to   { opacity: 0; transform: translate(0) scale(1); }
 }
 
 @keyframes downloadsIndicatorNotificationStartLeft {
   from { opacity: 0; transform: translate(128px, 128px) scale(8); }
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/downloads/indicator.inc.css
@@ -0,0 +1,21 @@
+/* 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/. */
+
+#downloads-indicator-icon {
+  position: relative;
+}
+
+#downloads-indicator-progress-icon {
+  background: var(--downloads-indicator-image-attention) bottom no-repeat;
+  background-size: 18px;
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+  height: 0;
+  transition: height 0.5s;
+}
+
+toolbar[brighttext] #downloads-indicator-progress-icon {
+  background-image: var(--downloads-indicator-image-attention-inverted);
+}
--- a/browser/themes/windows/downloads/indicator.css
+++ b/browser/themes/windows/downloads/indicator.css
@@ -1,12 +1,14 @@
 /* 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/. */
 
+%include ../../shared/downloads/indicator.inc.css
+
 /*** Status and progress indicator ***/
 
 #downloads-animation-container {
   min-height: 1px;
   min-width: 1px;
   height: 1px;
   margin-bottom: -1px;
   /* Makes the outermost animation container element positioned, so that its
@@ -16,41 +18,41 @@
   /* The selected tab may overlap #downloads-indicator-notification */
   z-index: 5;
 }
 
 /*** Main indicator icon ***/
 
 @media not all and (min-resolution: 1.1dppx) {
   #downloads-button {
-    --downloads-indicator-icon: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
-    --downloads-indicator-icon-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
-    --downloads-indicator-icon-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
-    --downloads-indicator-icon-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 0, 198, 18, 180);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), 18, 198, 36, 180);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 0, 198, 18, 180);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), 18, 198, 36, 180);
   }
 }
 
 @media (min-resolution: 1.1dppx) {
   #downloads-button {
-    --downloads-indicator-icon: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
-    --downloads-indicator-icon-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 36, 396, 72, 360);
-    --downloads-indicator-icon-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
-    --downloads-indicator-icon-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 36, 396, 72, 360);
+    --downloads-indicator-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 36, 396, 72, 360);
+    --downloads-indicator-image-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 0, 396, 36, 360);
+    --downloads-indicator-image-attention-inverted: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted@2x.png"), 36, 396, 72, 360);
   }
 }
 
 #downloads-indicator-icon {
-  background: var(--downloads-indicator-icon) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   width: 18px;
   height: 18px;
   background-size: 18px;
 }
 
 toolbar[brighttext] #downloads-button:not([attention="success"]) > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-inverted);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: -moz-box;
   height: 8px;
   width: 8px;
   min-width: 0;
@@ -74,46 +76,46 @@ toolbar[brighttext] #downloads-button:no
 }
 
 #downloads-button[attention="severe"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
 #downloads-button[attention="warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
   filter: none;
 }
 
 #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-attention);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon {
-  background-image: var(--downloads-indicator-icon-attention-inverted);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 #downloads-button[cui-areatype="menu-panel"][attention="success"] {
   list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel.png");
   -moz-image-region: auto;
 }
 
 /* In the next few rules, we use :not([counter]) as a shortcut that is
    equivalent to -moz-any([progress], [paused]). */
 
 #downloads-button:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background: var(--downloads-indicator-icon) center no-repeat;
+  background: var(--downloads-indicator-image) center no-repeat;
   background-size: 12px;
 }
 
 toolbar[brighttext] #downloads-button:not([counter]):not([attention="success"]) > #downloads-indicator-anchor > #downloads-button-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-inverted);
+  background-image: var(--downloads-indicator-image-inverted);
 }
 
 #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-attention);
+  background-image: var(--downloads-indicator-image-attention);
 }
 
 toolbar[brighttext] #downloads-button:not([counter])[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
-  background-image: var(--downloads-indicator-icon-attention-inverted);
+  background-image: var(--downloads-indicator-image-attention-inverted);
 }
 
 /*** Download notifications ***/
 
 #downloads-indicator-notification {
   opacity: 0;
   background-size: 16px;
   background-position: center;