Bug 1452629 - Part 1 - Avoid redundant button elements in the "download" widget. r=mak
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Thu, 13 Sep 2018 16:16:46 +0100
changeset 492148 58fc7f243f7605a4408454da153e40e27ecc3e81
parent 492147 1cae5a527a4b143a6ad6b2aaa5e5de03c43d2076
child 492149 de4eb68568a092a775de2c285b169f6e922eaa1a
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1452629
milestone64.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 1452629 - Part 1 - Avoid redundant button elements in the "download" widget. r=mak Differential Revision: https://phabricator.services.mozilla.com/D4441
browser/components/downloads/DownloadsViewUI.jsm
browser/components/downloads/content/allDownloadsView.js
browser/components/downloads/content/contentAreaDownloadsView.xul
browser/components/downloads/content/download.xml
browser/components/downloads/content/downloads.css
browser/components/downloads/content/downloads.js
browser/components/downloads/content/downloadsPanel.inc.xul
browser/components/downloads/content/downloadsStrings.inc.xul
browser/components/places/content/places.xul
browser/themes/shared/downloads/allDownloadsView.inc.css
browser/themes/shared/downloads/downloads.inc.css
--- a/browser/components/downloads/DownloadsViewUI.jsm
+++ b/browser/components/downloads/DownloadsViewUI.jsm
@@ -20,16 +20,54 @@ XPCOMUtils.defineLazyModuleGetters(this,
   Downloads: "resource://gre/modules/Downloads.jsm",
   DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
   DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
 });
 
+var gDownloadElementButtons = {
+  cancel: {
+    commandName: "downloadsCmd_cancel",
+    l10nId: "download-cancel",
+    iconClass: "downloadIconCancel",
+  },
+  retry: {
+    commandName: "downloadsCmd_retry",
+    l10nId: "download-retry",
+    iconClass: "downloadIconRetry",
+  },
+  show: {
+    commandName: "downloadsCmd_show",
+    l10nId: "download-show",
+    iconClass: "downloadIconShow",
+  },
+  subviewOpenOrRemoveFile: {
+    commandName: "downloadsCmd_showBlockedInfo",
+    l10nId: "download-open-or-remove-file",
+    iconClass: "downloadIconSubviewArrow",
+  },
+  askOpenOrRemoveFile: {
+    commandName: "downloadsCmd_chooseOpen",
+    l10nId: "download-open-or-remove-file",
+    iconClass: "downloadIconShow",
+  },
+  askRemoveFileOrAllow: {
+    commandName: "downloadsCmd_chooseUnblock",
+    l10nId: "download-remove-file-or-allow",
+    iconClass: "downloadIconShow",
+  },
+  removeFile: {
+    commandName: "downloadsCmd_confirmBlock",
+    l10nId: "download-remove-file",
+    iconClass: "downloadIconCancel",
+  },
+};
+
 var DownloadsViewUI = {
   /**
    * Returns true if the given string is the name of a command that can be
    * handled by the Downloads user interface, including standard commands.
    */
   isCommandName(name) {
     return name.startsWith("cmd_") || name.startsWith("downloadsCmd_");
   },
@@ -69,16 +107,27 @@ this.DownloadsViewUI.DownloadElementShel
 
 this.DownloadsViewUI.DownloadElementShell.prototype = {
   /**
    * The richlistitem for the download, initialized by the derived object.
    */
   element: null,
 
   /**
+   * Returns a string from the downloads stringbundleset, which contains legacy
+   * strings that are loaded from DTD files instead of properties files. This
+   * won't be necessary once localization is converted to Fluent (bug 1452637).
+   */
+  string(l10nId) {
+    // These strings are not used often enough to require caching.
+    return this.element.ownerDocument.getElementById("downloadsStrings")
+                                     .getAttribute("string-" + l10nId);
+  },
+
+  /**
    * URI string for the file type icon displayed in the download element.
    */
   get image() {
     if (!this.download.target.path) {
       // Old history downloads may not have a target path.
       return "moz-icon://.unknown?size=32";
     }
 
@@ -140,27 +189,67 @@ this.DownloadsViewUI.DownloadElementShel
       this.__progressElement =
            this.element.ownerDocument.getAnonymousElementByAttribute(
                                          this.element, "anonid",
                                          "progressmeter");
     }
     return this.__progressElement;
   },
 
+  showButton(type) {
+    let { commandName, l10nId, iconClass } = gDownloadElementButtons[type];
+
+    this.buttonCommandName = commandName;
+    let labelAttribute = this.isPanel ? "buttonarialabel" : "buttontooltiptext";
+    this.element.setAttribute(labelAttribute, this.string(l10nId));
+    this.element.setAttribute("buttonclass", "downloadButton " + iconClass);
+    this.element.removeAttribute("buttonhidden");
+  },
+
+  hideButton() {
+    this.element.setAttribute("buttonhidden", "true");
+  },
+
   /**
    * Processes a major state change in the user interface, then proceeds with
    * the normal progress update. This function is not called for every progress
    * update in order to improve performance.
    */
   _updateState() {
     this.element.setAttribute("displayName", this.displayName);
     this.element.setAttribute("image", this.image);
     this.element.setAttribute("state",
                               DownloadsCommon.stateOfDownload(this.download));
 
+    // We have to check for download state properties in the specific order used
+    // here, which is the same used by stateOfDownload.
+    if (!this.download.stopped) {
+      this.showButton("cancel");
+    } else if (this.download.succeeded) {
+      // The button is updated in _updateProgress, since its presence also
+      // depends on state that doesn't trigger _updateState when it changes.
+    } else if (this.download.error) {
+      if (this.download.error.becauseBlockedByParentalControls) {
+        this.hideButton();
+      } else if (this.download.error.becauseBlockedByReputationCheck) {
+        // The button is updated in _updateProgress, since its presence also
+        // depends on state that doesn't trigger _updateState when it changes.
+      } else {
+        this.showButton("retry");
+      }
+    } else if (this.download.canceled) {
+      if (this.download.hasPartialData) {
+        this.showButton("cancel");
+      } else {
+        this.showButton("retry");
+      }
+    } else {
+      this.showButton("cancel");
+    }
+
     if (!this.download.succeeded && this.download.error &&
         this.download.error.becauseBlockedByReputationCheck) {
       this.element.setAttribute("verdict",
                                 this.download.error.reputationCheckVerdict);
     } else {
       this.element.removeAttribute("verdict");
     }
 
@@ -170,22 +259,44 @@ this.DownloadsViewUI.DownloadElementShel
     this._updateProgress();
   },
 
   /**
    * Updates the elements that change regularly for in-progress downloads,
    * namely the progress bar and the status line.
    */
   _updateProgress() {
+    // Handle major state changes that don't trigger _updateState. These states
+    // don't occur for in-progress downloads, thus performance is not affected.
     if (this.download.succeeded) {
-      // We only need to add or remove this attribute for succeeded downloads.
       if (this.download.target.exists) {
         this.element.setAttribute("exists", "true");
+        this.showButton("show");
       } else {
         this.element.removeAttribute("exists");
+        this.hideButton();
+      }
+    } else if (this.download.error &&
+               this.download.error.becauseBlockedByReputationCheck) {
+      if (!this.download.hasBlockedData) {
+        this.hideButton();
+      } else if (this.isPanel) {
+        this.showButton("subviewOpenOrRemoveFile");
+      } else {
+        switch (this.download.error.reputationCheckVerdict) {
+          case Downloads.Error.BLOCK_VERDICT_UNCOMMON:
+            this.showButton("askOpenOrRemoveFile");
+            break;
+          case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
+            this.showButton("askRemoveFileOrAllow");
+            break;
+          default: // Assume Downloads.Error.BLOCK_VERDICT_MALWARE
+            this.showButton("removeFile");
+            break;
+        }
       }
     }
 
     // When a block is confirmed, the removal of blocked data will not trigger a
     // state change for the download, so this class must be updated here.
     this.element.classList.toggle("temporary-block",
                                   !!this.download.hasBlockedData);
 
@@ -424,16 +535,20 @@ this.DownloadsViewUI.DownloadElementShel
   },
 
   doCommand(aCommand) {
     if (DownloadsViewUI.isCommandName(aCommand)) {
       this[aCommand]();
     }
   },
 
+  onButton() {
+    this.doCommand(this.buttonCommandName);
+  },
+
   downloadsCmd_cancel() {
     // This is the correct way to avoid race conditions when cancelling.
     this.download.cancel().catch(() => {});
     this.download.removePartialData().catch(Cu.reportError);
   },
 
   downloadsCmd_confirmBlock() {
     this.download.confirmBlock().catch(Cu.reportError);
--- a/browser/components/downloads/content/allDownloadsView.js
+++ b/browser/components/downloads/content/allDownloadsView.js
@@ -177,18 +177,18 @@ HistoryDownloadElementShell.prototype = 
     }
   },
 };
 
 /**
  * Relays commands from the download.xml binding to the selected items.
  */
 const DownloadsView = {
-  onDownloadCommand(event, command) {
-    goDoCommand(command);
+  onDownloadButton(event) {
+    event.target.closest("richlistitem")._shell.onButton();
   },
 
   onDownloadClick() {},
 };
 
 /**
  * A Downloads Places View is a places view designed to show a places query
  * for history downloads alongside the session downloads.
--- a/browser/components/downloads/content/contentAreaDownloadsView.xul
+++ b/browser/components/downloads/content/contentAreaDownloadsView.xul
@@ -42,10 +42,11 @@
 
   <stack flex="1">
 #include downloadsRichListBox.inc.xul
     <description id="downloadsListEmptyDescription"
                  value="&downloadsListEmpty.label;"
                  mousethrough="always"/>
   </stack>
 #include downloadsCommands.inc.xul
+#include downloadsStrings.inc.xul
 #include downloadsContextMenu.inc.xul
 </window>
--- a/browser/components/downloads/content/download.xml
+++ b/browser/components/downloads/content/download.xml
@@ -75,43 +75,19 @@
                              value="&cancelDownload.label;"/>
             <xul:description class="downloadDetails downloadRetry"
                              crop="end"
                              value="&retryDownload.label;"/>
           </xul:stack>
         </xul:vbox>
       </xul:hbox>
       <xul:toolbarseparator />
-      <xul:stack class="downloadButtonArea">
-        <xul:button class="downloadButton downloadCancel downloadIconCancel"
-                    tooltiptext="&cmd.cancel.label;"
-                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/>
-        <xul:button class="downloadButton downloadRetry downloadIconRetry"
-                    tooltiptext="&cmd.retry.label;"
-                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/>
-        <xul:button class="downloadButton downloadShow downloadIconShow"
-#ifdef XP_MACOSX
-                    tooltiptext="&cmd.showMac.label;"
-#else
-                    tooltiptext="&cmd.show.label;"
-#endif
-                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_show');"/>
-        <xul:button class="downloadButton downloadConfirmBlock downloadIconCancel"
-                    tooltiptext="&cmd.removeFile.label;"
-                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_confirmBlock');"/>
-        <xul:button class="downloadButton downloadChooseUnblock downloadIconShow"
-                    tooltiptext="&cmd.chooseUnblock.label;"
-                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_chooseUnblock');"/>
-        <xul:button class="downloadButton downloadChooseOpen downloadIconShow"
-                    tooltiptext="&cmd.chooseOpen.label;"
-                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_chooseOpen');"/>
-        <xul:button class="downloadButton downloadShowBlockedInfo"
-                    tooltiptext="&cmd.chooseUnblock.label;"
-                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_showBlockedInfo');"/>
-      </xul:stack>
+      <xul:button class="downloadButton"
+                  xbl:inherits="class=buttonclass,aria-label=buttonarialabel,tooltiptext=buttontooltiptext"
+                  oncommand="DownloadsView.onDownloadButton(event);"/>
     </content>
   </binding>
 
   <binding id="download-subview-toolbarbutton"
            extends="chrome://global/content/bindings/button.xml#button-base">
     <content>
       <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label,consumeanchor"/>
       <xul:vbox class="toolbarbutton-text" flex="1">
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -8,21 +8,16 @@
   -moz-binding: url('chrome://browser/content/downloads/download.xml#download');
 }
 
 #downloadsListBox > richlistitem:not([selected]) button {
   /* Only focus buttons in the selected item. */
   -moz-user-focus: none;
 }
 
-#downloadsListBox > richlistitem.download-state[state="1"]:not([exists]) > .downloadButtonArea,
-#downloadsListBox > richlistitem.download-state[state="1"]:not([exists]) > toolbarseparator {
-  display: none;
-}
-
 #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress,
 #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails,
 #downloadsFooter:not([showingsummary]) #downloadsSummary {
   display: none;
 }
 
 #downloadsFooter[showingsummary] > stack:hover > #downloadsSummary,
 #downloadsFooter[showingsummary] > stack:not(:hover) > #downloadsFooterButtons {
@@ -60,16 +55,22 @@
 #downloadsRichListBox > richlistitem button {
   /* These buttons should never get focus, as that would "disable"
      the downloads view controller (it's only used when the richlistbox
      is focused). */
   -moz-user-focus: none;
 }
 
 /*** Visibility of controls inside download items ***/
+
+.download-state[buttonhidden] > toolbarseparator,
+.download-state[buttonhidden] > .downloadButton {
+  display: none;
+}
+
 .download-state:not(:-moz-any([state="6"], /* Blocked (parental) */
                               [state="8"], /* Blocked (dirty)    */
                               [state="9"]) /* Blocked (policy)   */)
                                            .downloadBlockedBadge,
 
 .download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
                               [state="5"], /* Starting (queued)  */
                               [state="0"], /* Downloading        */
@@ -118,100 +119,50 @@
                                            .downloadCommandsSeparator,
 .download-state[state="8"]:not(.temporary-block)
                                            .downloadCommandsSeparator
 
 {
   display: none;
 }
 
-/*** Visibility of download buttons ***/
+/*** Visibility of download button labels ***/
 
 .download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
                               [state="5"], /* Starting (queued)  */
                               [state="0"], /* Downloading        */
                               [state="4"]) /* Paused             */)
                                            .downloadCancel,
 
-/* Blocked (dirty) downloads that have not been confirmed and
-   have temporary data, for the Malware case. */
-.download-state:not(          [state="8"]  /* Blocked (dirty)    */)
-                                           .downloadConfirmBlock,
-.download-state[state="8"]:not(.temporary-block)
-                                           .downloadConfirmBlock,
-.download-state[state="8"].temporary-block:not([verdict="Malware"])
-                                           .downloadConfirmBlock,
-
-/* Blocked (dirty) downloads that have not been confirmed and
-   have temporary data, for the Potentially Unwanted case. */
-.download-state:not(          [state="8"]  /* Blocked (dirty)    */)
-                                           .downloadChooseUnblock,
-.download-state[state="8"]:not(.temporary-block)
-                                           .downloadChooseUnblock,
-.download-state[state="8"].temporary-block:not([verdict="PotentiallyUnwanted"])
-                                           .downloadChooseUnblock,
-
-/* Blocked (dirty) downloads that have not been confirmed and
-   have temporary data, for the Uncommon case. */
-.download-state:not(          [state="8"]  /* Blocked (dirty)    */)
-                                           .downloadChooseOpen,
-.download-state[state="8"]:not(.temporary-block)
-                                           .downloadChooseOpen,
-.download-state[state="8"].temporary-block:not([verdict="Uncommon"])
-                                           .downloadChooseOpen,
-
 .download-state:not(:-moz-any([state="2"], /* Failed             */
                               [state="3"]) /* Canceled           */)
                                            .downloadRetry,
 
 .download-state:not(          [state="1"]  /* Finished           */)
-                                           .downloadShow,
-
-.download-state:-moz-any(     [state="6"], /* Blocked (parental) */
-                              [state="7"], /* Scanning           */
-                              [state="9"]) /* Blocked (policy)   */
-                                           > toolbarseparator,
-
-/* The "show blocked info" button is shown only in the downloads panel. */
-.downloadShowBlockedInfo
+                                           .downloadShow
 {
   display: none;
 }
 
 /*** Downloads panel ***/
 
 #downloadsPanel[hasdownloads] #emptyDownloads,
 #downloadsPanel:not([hasdownloads]) #downloadsListBox {
   display: none;
 }
 
 /*** Downloads panel multiview (main view and blocked-downloads subview) ***/
 
-/* Hide all the usual buttons. */
-#downloadsPanel-mainView .download-state[state="8"] .downloadCancel,
-#downloadsPanel-mainView .download-state[state="8"] .downloadConfirmBlock,
-#downloadsPanel-mainView .download-state[state="8"] .downloadChooseUnblock,
-#downloadsPanel-mainView .download-state[state="8"] .downloadChooseOpen,
-#downloadsPanel-mainView .download-state[state="8"] .downloadRetry,
-#downloadsPanel-mainView .download-state[state="8"] .downloadShow {
-  display: none;
-}
-
 /* Make the panel wide enough to show the download list items without improperly
    truncating them. */
 #downloadsPanel-multiView > .panel-viewcontainer,
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack {
   max-width: unset;
 }
 
-/* Show the "show blocked info" button. */
-#downloadsPanel-mainView .download-state[state="8"] .downloadShowBlockedInfo {
-  display: inline;
-}
-
 /* DownloadsSubview styles: */
 
 .subviewbutton.download {
   -moz-binding: url("chrome://browser/content/downloads/download.xml#download-subview-toolbarbutton");
 }
 
 /* Hide all status labels by default and selectively display one at a time,
    depending on the state of the Download. */
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -747,34 +747,16 @@ var DownloadsView = {
                                                 this.richListBox.itemCount - 1);
     }
     this._visibleViewItems.delete(download);
     this._itemsForElements.delete(element);
   },
 
   // User interface event functions
 
-  /**
-   * Helper function to do commands on a specific download item.
-   *
-   * @param aEvent
-   *        Event object for the event being handled.  If the event target is
-   *        not a richlistitem that represents a download, this function will
-   *        walk up the parent nodes until it finds a DOM node that is.
-   * @param aCommand
-   *        The command to be performed.
-   */
-  onDownloadCommand(aEvent, aCommand) {
-    let target = aEvent.target;
-    while (target.nodeName != "richlistitem") {
-      target = target.parentNode;
-    }
-    DownloadsView.itemForElement(target).doCommand(aCommand);
-  },
-
   onDownloadClick(aEvent) {
     // Handle primary clicks only, and exclude the action button.
     if (aEvent.button == 0 &&
         !aEvent.originalTarget.hasAttribute("oncommand")) {
       let target = aEvent.target;
       while (target.nodeName != "richlistitem") {
         target = target.parentNode;
       }
@@ -782,16 +764,21 @@ var DownloadsView = {
       if (download.hasBlockedData) {
         goDoCommand("downloadsCmd_showBlockedInfo");
       } else {
         goDoCommand("downloadsCmd_open");
       }
     }
   },
 
+  onDownloadButton(event) {
+    let target = event.target.closest("richlistitem");
+    DownloadsView.itemForElement(target).onButton();
+  },
+
   /**
    * Handles keypress events on a download item.
    */
   onDownloadKeyPress(aEvent) {
     // Pressing the key on buttons should not invoke the action because the
     // event has already been handled by the button itself.
     if (aEvent.originalTarget.hasAttribute("command") ||
         aEvent.originalTarget.hasAttribute("oncommand")) {
@@ -833,23 +820,16 @@ var DownloadsView = {
   },
 
   /**
    * Mouse listeners to handle selection on hover.
    */
   onDownloadMouseOver(aEvent) {
     if (aEvent.originalTarget.classList.contains("downloadButton")) {
       aEvent.target.classList.add("downloadHoveringButton");
-
-      let button = aEvent.originalTarget;
-      let tooltip = button.getAttribute("tooltiptext");
-      if (tooltip) {
-        button.setAttribute("aria-label", tooltip);
-        button.removeAttribute("tooltiptext");
-      }
     }
     if (!(this.contextMenuOpen || this.subViewOpen) &&
         aEvent.target.parentNode == this.richListBox) {
       this.richListBox.selectedItem = aEvent.target;
     }
   },
 
   onDownloadMouseOut(aEvent) {
@@ -933,16 +913,18 @@ function DownloadsViewItem(download, aEl
   this.download = download;
   this.downloadState = DownloadsCommon.stateOfDownload(download);
   this.element = aElement;
   this.element._shell = this;
 
   this.element.setAttribute("type", "download");
   this.element.classList.add("download-state");
 
+  this.isPanel = true;
+
   this._updateState();
 }
 
 DownloadsViewItem.prototype = {
   __proto__: DownloadsViewUI.DownloadElementShell.prototype,
 
   /**
    * The XUL element corresponding to the associated richlistbox item.
--- a/browser/components/downloads/content/downloadsPanel.inc.xul
+++ b/browser/components/downloads/content/downloadsPanel.inc.xul
@@ -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 downloadsStrings.inc.xul
+
 <commandset>
   <command id="downloadsCmd_doDefault"
            oncommand="goDoCommand('downloadsCmd_doDefault')"/>
   <command id="downloadsCmd_pauseResume"
            oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
   <command id="downloadsCmd_cancel"
            oncommand="goDoCommand('downloadsCmd_cancel')"/>
   <command id="downloadsCmd_unblock"
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/downloadsStrings.inc.xul
@@ -0,0 +1,15 @@
+# Don't add new strings here, use properties files instead. This file won't be
+# necessary anymore once localization is converted to Fluent (bug 1452637).
+
+<stringbundleset id="downloadsStrings"
+  string-download-cancel="&cmd.cancel.label;"
+  string-download-open-or-remove-file="&cmd.chooseOpen.label;"
+  string-download-remove-file="&cmd.removeFile.label;"
+  string-download-remove-file-or-allow="&cmd.chooseUnblock.label;"
+  string-download-retry="&cmd.retry.label;"
+#ifdef XP_MACOSX
+  string-download-show="&cmd.showMac.label;"
+#else
+  string-download-show="&cmd.show.label;"
+#endif
+/>
--- a/browser/components/places/content/places.xul
+++ b/browser/components/places/content/places.xul
@@ -90,16 +90,17 @@
     <command id="OrganizerCommand_search:moreCriteria"
              oncommand="PlacesQueryBuilder.addRow();"/>
     <command id="OrganizerCommand:Back"
              oncommand="PlacesOrganizer.back();"/>
     <command id="OrganizerCommand:Forward"
              oncommand="PlacesOrganizer.forward();"/>
   </commandset>
 #include ../../downloads/content/downloadsCommands.inc.xul
+#include ../../downloads/content/downloadsStrings.inc.xul
 
   <keyset id="placesOrganizerKeyset">
     <!-- Instantiation Keys -->
     <key id="placesKey_close" key="&cmd.close.key;" modifiers="accel"
          oncommand="close();"/>
 
     <!-- Command Keys -->
     <key id="placesKey_find:all"
--- a/browser/themes/shared/downloads/allDownloadsView.inc.css
+++ b/browser/themes/shared/downloads/allDownloadsView.inc.css
@@ -102,22 +102,22 @@
   color: -moz-field;
   border-radius: 50%;
 }
 
 .downloadButton:hover:active > .button-box {
   background-color: -moz-fieldtext;
 }
 
-@itemFocused@ > .downloadButtonArea > .downloadButton:hover > .button-box {
+@itemFocused@ > .downloadButton:hover > .button-box {
   background-color: HighlightText;
   color: Highlight;
 }
 
-@itemFocused@ > .downloadButtonArea > .downloadButton:hover:active > .button-box {
+@itemFocused@ > .downloadButton:hover:active > .button-box {
   background-color: -moz-field;
   color: -moz-fieldtext;
 }
 
 /*** Button icons ***/
 
 .downloadIconCancel > .button-box > .button-icon {
   list-style-image: url("chrome://browser/skin/panel-icon-cancel.svg");
--- a/browser/themes/shared/downloads/downloads.inc.css
+++ b/browser/themes/shared/downloads/downloads.inc.css
@@ -247,23 +247,23 @@
 }
 
 .downloadButton > .button-box > .button-text {
   margin: 0 !important;
   padding: 0;
 }
 
 @itemFinished@[exists] .downloadMainArea:hover,
-@item@:not([verdict]) > .downloadButtonArea:hover,
+@item@:not([verdict]) > .downloadButton:hover,
 @item@[verdict]:hover {
   background-color: var(--arrowpanel-dimmed);
 }
 
 @itemFinished@[exists] > .downloadMainArea:hover:active,
-@item@:not([verdict]) > .downloadButtonArea:hover:active,
+@item@:not([verdict]) > .downloadButton:hover:active,
 @item@[verdict]:hover:active {
   background-color: var(--arrowpanel-dimmed-further);
 }
 
 @item@[verdict="Malware"]:hover,
 @item@[verdict="Malware"]:hover:active {
   background-color: #aa1b08;
   color: white;
@@ -282,21 +282,21 @@
   list-style-image: url("chrome://browser/skin/panel-icon-folder.svg");
 %endif
 }
 
 .downloadIconRetry > .button-box > .button-icon {
   list-style-image: url("chrome://browser/skin/panel-icon-retry.svg");
 }
 
-.downloadShowBlockedInfo > .button-box > .button-icon {
+.downloadIconSubviewArrow > .button-box > .button-icon {
   list-style-image: url("chrome://browser/skin/panel-icon-arrow-right.svg");
 }
 
-.downloadShowBlockedInfo > .button-box > .button-icon:-moz-locale-dir(rtl) {
+.downloadIconSubviewArrow > .button-box > .button-icon:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/panel-icon-arrow-left.svg");
 }
 
 /*** Blocked subview ***/
 
 #downloadsPanel-blockedSubview > .panel-view-body-unscrollable {
   background-image: url("chrome://browser/skin/warning.svg");
   background-size: 32px 32px;