Bug 828232 - Allow to retry history downloads.
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 10 Jan 2013 12:08:57 +0100
changeset 127057 b170785e88b2f1746542f384d2a7468abe3e468f
parent 127056 4f9fb288efc8e0e251cc4079c5dfe87ee25c6cf2
child 127058 33bc3c8f5d4f17383149172fa92f73e7687437e4
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs828232
milestone20.0a2
Bug 828232 - Allow to retry history downloads. r=Mano a=gavin
browser/components/downloads/content/allDownloadsViewOverlay.js
browser/components/downloads/content/download.css
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -17,16 +17,18 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/DownloadUtils.jsm");
 Cu.import("resource:///modules/DownloadsCommon.jsm");
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
+                                  "resource:///modules/RecentWindow.jsm");
 
 const nsIDM = Ci.nsIDownloadManager;
 
 const DESTINATION_FILE_URI_ANNO  = "downloads/destinationFileURI";
 const DESTINATION_FILE_NAME_ANNO = "downloads/destinationFileName";
 const DOWNLOAD_STATE_ANNO        = "downloads/state";
 
 const DOWNLOAD_VIEW_SUPPORTED_COMMANDS =
@@ -384,23 +386,22 @@ DownloadElementShell.prototype = {
       }
     }
 
     return s.sizeUnknown;
   },
 
   // The progressmeter element for the download
   get _progressElement() {
-    let progressElement = document.getAnonymousElementByAttribute(
-      this._element, "anonid", "progressmeter");
-    if (progressElement) {
-      delete this._progressElement;
-      return this._progressElement = progressElement;
+    if (!("__progressElement" in this)) {
+      this.__progressElement =
+        document.getAnonymousElementByAttribute(this._element, "anonid",
+                                                "progressmeter");
     }
-    return null;
+    return this.__progressElement;
   },
 
   // Updates the download state attribute (and by that hide/unhide the
   // appropriate buttons and context menu items), the status text label,
   // and the progress meter.
   _updateDownloadStatusUI: function  DES__updateDownloadStatusUI() {
     let state = this.getDownloadState(true);
     if (state !== undefined)
@@ -528,34 +529,42 @@ DownloadElementShell.prototype = {
         if (!this._targetFileInfoFetched)
           return false;
 
         return this._targetFileExists;
       }
       case "downloadsCmd_pauseResume":
         return this._dataItem && this._dataItem.inProgress && this._dataItem.resumable;
       case "downloadsCmd_retry":
-        // Disable the retry command for past downloads until it's fully implemented.
-        return this._dataItem && this._dataItem.canRetry;
+        // An history download can always be retried.
+        return !this._dataItem || this._dataItem.canRetry;
       case "downloadsCmd_openReferrer":
         return this._dataItem && !!this._dataItem.referrer;
       case "cmd_delete":
         // The behavior in this case is somewhat unexpected, so we disallow that.
         if (this._placesNode && this._dataItem && this._dataItem.inProgress)
           return false;
         return true;
       case "downloadsCmd_cancel":
         return this._dataItem != null;
     }
     return false;
   },
 
   _retryAsHistoryDownload: function DES__retryAsHistoryDownload() {
-    // TODO: save in the right location (the current saveURL api does not allow this)
-    saveURL(this.downloadURI, this._displayName, null, true, true, undefined, document);
+    // In future we may try to download into the same original target uri, when
+    // we have it.  Though that requires verifying the path is still valid and
+    // may surprise the user if he wants to be requested every time.
+
+    // For private browsing, try to get document out of the most recent browser
+    // window, or provide our own if there's no browser window.
+    let browserWin = RecentWindow.getMostRecentBrowserWindow();
+    let initiatingDoc = browserWin ? browserWin.document : document;
+    saveURL(this.downloadURI, this._displayName, null, true, true, undefined,
+            initiatingDoc);
   },
 
   /* nsIController */
   doCommand: function DES_doCommand(aCommand) {
     switch (aCommand) {
       case "downloadsCmd_open": {
         if (this._dateItem)
           this._dataItem.openLocalFile(window);
@@ -835,16 +844,20 @@ DownloadsPlacesView.prototype = {
 
     if (newOrUpdatedShell) {
       if (aNewest) {
         this._richlistbox.insertBefore(newOrUpdatedShell.element,
                                        this._richlistbox.firstChild);
         if (!this._lastSessionDownloadElement) {
           this._lastSessionDownloadElement = newOrUpdatedShell.element;
         }
+        // Some operations like retrying an history download move an element to
+        // the top of the richlistbox, along with other session downloads.
+        // More generally, if a new download is added, should be made visible.
+        this._richlistbox.ensureElementIsVisible(newOrUpdatedShell.element);
       }
       else if (aDataItem) {
         let before = this._lastSessionDownloadElement ?
           this._lastSessionDownloadElement.nextSibling : this._richlistbox.firstChild;
         this._richlistbox.insertBefore(newOrUpdatedShell.element, before);
         this._lastSessionDownloadElement = newOrUpdatedShell.element;
       }
       else {
@@ -932,23 +945,25 @@ DownloadsPlacesView.prototype = {
 
   _ensureVisibleElementsAreActive:
   function DPV__ensureVisibleElementsAreActive() {
     if (!this.active || this._ensureVisibleTimer || !this._richlistbox.firstChild)
       return;
 
     this._ensureVisibleTimer = setTimeout(function() {
       delete this._ensureVisibleTimer;
+      if (!this._richlistbox.firstChild)
+        return;
 
       let rlRect = this._richlistbox.getBoundingClientRect();
       let fcRect = this._richlistbox.firstChild.getBoundingClientRect();
       // For simplicity assume border and padding are the same across all sides.
       // This works as far as there isn't an horizontal scrollbar since fcRect
       // is relative to the scrolled area.
-      let offset = fcRect.left - rlRect.left + 1;
+      let offset = (fcRect.left - rlRect.left) + 1;
 
       let firstVisible = document.elementFromPoint(fcRect.left, rlRect.top + offset);
       if (!firstVisible || firstVisible.localName != "richlistitem")
         throw new Error("_ensureVisibleElementsAreActive invoked on the wrong view");
 
       let lastVisible = document.elementFromPoint(fcRect.left, rlRect.bottom - offset);
       // If the last visible child found is not a richlistitem, then there are
       // less items than the available space, thus just proceed to the last child.
--- a/browser/components/downloads/content/download.css
+++ b/browser/components/downloads/content/download.css
@@ -9,39 +9,37 @@ richlistitem.download button {
   -moz-user-focus: none;
 }
 
 /*** Visibility of controls inside download items ***/
 
 .download-state:-moz-any(     [state="6"], /* Blocked (parental) */
                               [state="8"], /* Blocked (dirty)    */
                               [state="9"]) /* Blocked (policy)   */
-                                           .downloadTypeIcon:not(.blockedIcon),
+                                           > .downloadTypeIcon:not(.blockedIcon),
 
 .download-state:not(:-moz-any([state="6"], /* Blocked (parental) */
                               [state="8"], /* Blocked (dirty)    */
                               [state="9"]) /* Blocked (policy)   */)
-                                           .downloadTypeIcon.blockedIcon,
+                                           > .downloadTypeIcon.blockedIcon,
 
 .download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
                               [state="5"], /* Starting (queued)  */
                               [state="0"], /* Downloading        */
                               [state="4"], /* Paused             */
                               [state="7"]) /* Scanning           */)
-                                           .downloadProgress,
+                                           > vbox > .downloadProgress,
 
 .download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
                               [state="5"], /* Starting (queued)  */
                               [state="0"], /* Downloading        */
                               [state="4"]) /* Paused             */)
-                                           .downloadCancel,
+                                           > .downloadCancel,
 
-.download-state:not(:-moz-any([state="2"], /* Failed             */
-                              [state="3"]) /* Canceled           */)
-                                           .downloadRetry,
+.download-state[state]:not(:-moz-any([state="2"], /* Failed             */
+                                     [state="3"]) /* Canceled           */)
+                                                  > .downloadRetry,
 
 .download-state:not(          [state="1"]  /* Finished           */)
-                                           .downloadShow,
-
-.download-state:not([state]) > button
+                                           > .downloadShow
 {
   display: none;
 }