Bug 832672 - Downloads Panel gives no indication or feedback on missing files. r=mak ui-r=shorlander
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Wed, 24 Apr 2013 02:19:49 +0200
changeset 140645 12b4820314691fa5d842782723148f5dee1368bd
parent 140644 5c7e9f21c920b379142d785aba4c141217198704
child 140646 e63dfc0d3607ba495093fd8dab9276b098a6885b
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak, shorlander
bugs832672
milestone23.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 832672 - Downloads Panel gives no indication or feedback on missing files. r=mak ui-r=shorlander
browser/components/downloads/content/downloads.css
browser/components/downloads/content/downloads.js
browser/themes/linux/downloads/downloads.css
browser/themes/osx/downloads/downloads.css
browser/themes/windows/downloads/downloads-aero.css
browser/themes/windows/downloads/downloads.css
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -86,16 +86,21 @@ richlistitem[type="download"]:not([selec
                                    [counter],
                                    [paused]))
                                            #downloads-indicator-progress-area
 
 {
   visibility: hidden;
 }
 
+.download-state[state="1"]:not([exists]) .downloadShow
+{
+  display: none;
+}
+
 #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress,
 #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails,
 #downloadsFooter[showingsummary] > #downloadsHistory,
 #downloadsFooter:not([showingsummary]) > #downloadsSummary
 {
   display: none;
 }
 
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -66,16 +66,18 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
                                   "resource://gre/modules/DownloadUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
                                   "resource:///modules/DownloadsCommon.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+                                  "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -548,16 +550,24 @@ const DownloadsPanel = {
       // close the panel anymore.  To prevent this, check if the window is
       // minimized and in that case force the panel to the closed state.
       if (window.windowState == Ci.nsIDOMChromeWindow.STATE_MINIMIZED) {
         DownloadsButton.releaseAnchor();
         this._state = this.kStateHidden;
         return;
       }
 
+      // When the panel is opened, we check if the target files of visible items
+      // still exist, and update the allowed items interactions accordingly.  We
+      // do these checks on a background thread, and don't prevent the panel to
+      // be displayed while these checks are being performed.
+      for each (let viewItem in DownloadsView._viewItems) {
+        viewItem.verifyTargetExists();
+      }
+
       if (aAnchor) {
         DownloadsCommon.log("Opening downloads panel popup.");
         this.panel.openPopup(aAnchor, "bottomcenter topright", 0, 0, false,
                              null);
       } else {
         DownloadsCommon.error("We can't find the anchor! Failure case - opening",
                               "downloads panel on TabsToolbar. We should never",
                               "get here!");
@@ -1069,16 +1079,17 @@ function DownloadsViewItem(aDataItem, aE
 
   for (let attributeName in attributes) {
     this._element.setAttribute(attributeName, attributes[attributeName]);
   }
 
   // Initialize more complex attributes.
   this._updateProgress();
   this._updateStatusLine();
+  this.verifyTargetExists();
 }
 
 DownloadsViewItem.prototype = {
   /**
    * The DownloadDataItem associated with this view item.
    */
   dataItem: null,
 
@@ -1106,16 +1117,22 @@ DownloadsViewItem.prototype = {
     // now exists and we can extract its specific icon.  To ensure that the icon
     // is reloaded, we must change the URI used by the XUL image element, for
     // example by adding a query parameter.  Since this URI has a "moz-icon"
     // scheme, this only works if we add one of the parameters explicitly
     // supported by the nsIMozIconURI interface.
     if (aOldState != Ci.nsIDownloadManager.DOWNLOAD_FINISHED &&
         aOldState != this.dataItem.state) {
       this._element.setAttribute("image", this.image + "&state=normal");
+
+      // We assume the existence of the target of a download that just completed
+      // successfully, without checking the condition in the background.  If the
+      // panel is already open, this will take effect immediately.  If the panel
+      // is opened later, a new background existence check will be performed.
+      this._element.setAttribute("exists", "true");
     }
 
     // Update the user interface after switching states.
     this._element.setAttribute("state", this.dataItem.state);
     this._updateProgress();
     this._updateStatusLine();
   },
 
@@ -1247,17 +1264,43 @@ DownloadsViewItem.prototype = {
   {
     // Display the file size, but show "Unknown" for negative sizes.
     let fileSize = this.dataItem.maxBytes;
     if (fileSize < 0) {
       return DownloadsCommon.strings.sizeUnknown;
     }
     let [size, unit] = DownloadUtils.convertByteUnits(fileSize);
     return DownloadsCommon.strings.sizeWithUnits(size, unit);
-  }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Functions called by the panel
+
+  /**
+   * Starts checking whether the target file of a finished download is still
+   * available on disk, and sets an attribute that controls how the item is
+   * presented visually.
+   *
+   * The existence check is executed on a background thread.
+   */
+  verifyTargetExists: function DVI_verifyTargetExists() {
+    // We don't need to check if the download is not finished successfully.
+    if (!this.dataItem.openable) {
+      return;
+    }
+
+    OS.File.exists(this.dataItem.localFile.path).then(
+      function DVI_RTE_onSuccess(aExists) {
+        if (aExists) {
+          this._element.setAttribute("exists", "true");
+        } else {
+          this._element.removeAttribute("exists");
+        }
+      }.bind(this), Cu.reportError);
+  },
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsViewController
 
 /**
  * Handles part of the user interaction events raised by the downloads list
  * widget, in particular the "commands" that apply to multiple items, and
--- a/browser/themes/linux/downloads/downloads.css
+++ b/browser/themes/linux/downloads/downloads.css
@@ -150,17 +150,17 @@ richlistitem[type="download"]:last-child
 }
 
 .downloadButton:focus > .button-box {
   outline: 1px -moz-dialogtext dotted;
 }
 
 /*** Highlighted list items ***/
 
-#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover {
+#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover {
   border-radius: 3px;
   border-top: 1px solid hsla(0,0%,100%,.3);
   border-bottom: 1px solid hsla(0,0%,0%,.2);
   background-color: Highlight;
   background-image: linear-gradient(hsla(0,0%,100%,.1), hsla(0,0%,100%,0));
   color: HighlightText;
   cursor: pointer;
 }
--- a/browser/themes/osx/downloads/downloads.css
+++ b/browser/themes/osx/downloads/downloads.css
@@ -172,25 +172,25 @@ richlistitem[type="download"]:last-child
 
 .downloadButton > .button-box {
   padding: 0;
 }
 
 /*** Highlighted list items ***/
 
 #downloadsPanel[keyfocus] > #downloadsListBox:focus > richlistitem[type="download"][selected],
-#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover {
+#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover {
   border-radius: 3px;
   border-top: 1px solid hsla(0,0%,100%,.2);
   border-bottom: 1px solid hsla(0,0%,0%,.4);
   background-color: Highlight;
   color: HighlightText;
 }
 
-#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover {
+#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover {
   cursor: pointer;
 }
 
 /*** Button icons ***/
 
 .downloadButton.downloadCancel {
   -moz-image-region: rect(0px, 16px, 16px, 0px);
 }
--- a/browser/themes/windows/downloads/downloads-aero.css
+++ b/browser/themes/windows/downloads/downloads-aero.css
@@ -7,17 +7,17 @@
 %undef WINDOWS_AERO
 
 @media (-moz-windows-default-theme) {
   richlistitem[type="download"] {
     border: 1px solid transparent;
     border-bottom: 1px solid hsl(213,40%,90%);
   }
 
-  #downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover {
+  #downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover {
     border: 1px solid hsl(213,45%,65%);
     box-shadow: 0 0 0 1px hsla(0,0%,100%,.5) inset,
                 0 1px 0 hsla(0,0%,100%,.3) inset;
     background-image: linear-gradient(hsl(212,86%,92%), hsl(212,91%,86%));
     color: black;
   }
 }
 
--- a/browser/themes/windows/downloads/downloads.css
+++ b/browser/themes/windows/downloads/downloads.css
@@ -161,17 +161,17 @@ richlistitem[type="download"]:first-chil
 }
 
 #downloadsPanel[keyfocus] .downloadButton:focus > .button-box {
   border: 1px dotted ThreeDDarkShadow;
 }
 
 /*** Highlighted list items ***/
 
-#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover {
+#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover {
   border-radius: 3px;
   border-top: 1px solid hsla(0,0%,100%,.2);
   border-bottom: 1px solid hsla(0,0%,0%,.2);
   background-color: Highlight;
   color: HighlightText;
   cursor: pointer;
 }