Bug 759397 - Remove per-item download rate clutter from Downloads Panel. r=mak.
authorMike Conley <mconley@mozilla.com>
Tue, 06 Nov 2012 13:21:50 -0500
changeset 112412 bd648e7c2656938a5e34b3412128302c8fc54f0e
parent 112411 358efbbf8c08bba01a859cdd577bf6717af04ae3
child 112413 235c7f72a92beeb4873e1919d8387a4848b30e11
push id17580
push usermconley@mozilla.com
push dateTue, 06 Nov 2012 18:22:10 +0000
treeherdermozilla-inbound@bd648e7c2656 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs759397
milestone19.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 759397 - Remove per-item download rate clutter from Downloads Panel. r=mak.
browser/components/downloads/content/download.xml
browser/components/downloads/content/downloads.js
browser/components/downloads/content/downloadsOverlay.xul
browser/locales/en-US/chrome/browser/downloads/downloads.dtd
toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties
toolkit/mozapps/downloads/DownloadUtils.jsm
toolkit/mozapps/downloads/tests/unit/test_DownloadUtils.js
--- a/browser/components/downloads/content/download.xml
+++ b/browser/components/downloads/content/download.xml
@@ -28,16 +28,17 @@
                          crop="center"
                          xbl:inherits="value=target,tooltiptext=target"/>
         <xul:progressmeter anonid="progressmeter"
                            class="downloadProgress"
                            min="0"
                            max="100"
                            xbl:inherits="mode=progressmode,value=progress"/>
         <xul:description class="downloadDetails"
+                         style="width: &downloadDetails.width;"
                          crop="end"
                          xbl:inherits="value=status,tooltiptext=statusTip"/>
       </xul:vbox>
       <xul:button class="downloadButton downloadCancel"
                   tooltiptext="&cmd.cancel.label;"
                   oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/>
       <xul:button class="downloadButton downloadRetry"
                   tooltiptext="&cmd.retry.label;"
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -916,18 +916,28 @@ DownloadsViewItem.prototype = {
                                                     this.dataItem.maxBytes);
 
       // We use the same XUL label to display both the state and the amount
       // transferred, for example "Paused -  1.1 MB".
       status = DownloadsCommon.strings.statusSeparatorBeforeNumber(
                                             DownloadsCommon.strings.statePaused,
                                             transfer);
     } else if (this.dataItem.state == nsIDM.DOWNLOAD_DOWNLOADING) {
+      // We don't show the rate for each download in order to reduce clutter.
+      // The remaining time per download is likely enough information for the
+      // panel.
+      [status] =
+        DownloadUtils.getDownloadStatusNoRate(this.dataItem.currBytes,
+                                              this.dataItem.maxBytes,
+                                              this.dataItem.speed,
+                                              this.lastEstimatedSecondsLeft);
+
+      // We are, however, OK with displaying the rate in the tooltip.
       let newEstimatedSecondsLeft;
-      [status, newEstimatedSecondsLeft] =
+      [statusTip, newEstimatedSecondsLeft] =
         DownloadUtils.getDownloadStatus(this.dataItem.currBytes,
                                         this.dataItem.maxBytes,
                                         this.dataItem.speed,
                                         this.lastEstimatedSecondsLeft);
       this.lastEstimatedSecondsLeft = newEstimatedSecondsLeft;
     } else if (this.dataItem.starting) {
       status = DownloadsCommon.strings.stateStarting;
     } else if (this.dataItem.state == nsIDM.DOWNLOAD_SCANNING) {
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -93,17 +93,16 @@
         <menuseparator/>
 
         <menuitem command="downloadsCmd_clearList"
                   label="&cmd.clearList.label;"
                   accesskey="&cmd.clearList.accesskey;"/>
       </menupopup>
 
       <richlistbox id="downloadsListBox"
-                   style="width: &downloads.width;"
                    class="plain"
                    flex="1"
                    context="downloadsContextMenu"
                    onkeypress="DownloadsView.onDownloadKeyPress(event);"
                    oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
                    ondragstart="DownloadsView.onDownloadDragStart(event);"/>
 
       <button id="downloadsHistory"
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
@@ -7,22 +7,33 @@
      -->
 <!ENTITY indicator.tooltiptext            "Downloads">
 
 <!-- LOCALIZATION NOTE (downloads.title):
      Used by screen readers to describe the Downloads Panel.
      -->
 <!ENTITY downloads.title                  "Downloads">
 
-<!-- LOCALIZATION NOTE (downloads.width):
-     Width of the Downloads Panel list items, expressed using a CSS unit.  The
-     longest labels that should fit in the item width are usually those of
-     in-progress downloads and those of blocked downloads.
+<!-- LOCALIZATION NOTE (downloadDetails.width):
+     Width of details for a Downloads Panel item (which directly influences the
+     width of the Downloads Panel) expressed using a CSS unit. The longest
+     labels that should fit in the item width are usually those of in-progress
+     downloads and those of blocked downloads.
+
+     A good rule of thumb is to try to determine the longest string possible
+     that an in-progress download could display, and use that value in ch
+     units.
+
+     For example, in English, a long string would be:
+
+     59 minutes, 59 seconds remaining - 1022 of 1023 KB
+
+     That's 50 characters, so we set the width at 50ch.
      -->
-<!ENTITY downloads.width                  "65ch">
+<!ENTITY downloadDetails.width            "50ch">
 
 <!ENTITY cmd.pause.label                  "Pause">
 <!ENTITY cmd.pause.accesskey              "P">
 <!ENTITY cmd.resume.label                 "Resume">
 <!ENTITY cmd.resume.accesskey             "R">
 <!ENTITY cmd.cancel.label                 "Cancel">
 <!ENTITY cmd.cancel.accesskey             "C">
 <!-- LOCALIZATION NOTE (cmd.show.label, cmd.show.accesskey, cmd.showMac.label,
--- a/toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties
@@ -45,16 +45,22 @@ dontEnterPrivateBrowsingButton=Don't Ent
 dontLeavePrivateBrowsingButton=Stay in Private Browsing Mode
 downloadsCompleteTitle=Downloads Complete
 downloadsCompleteMsg=All files have finished downloading. 
 
 # LOCALIZATION NOTE (statusFormat3): — is the "em dash" (long dash)
 # %1$S transfer progress; %2$S rate number; %3$S rate unit; %4$S time left
 # example: 4 minutes left — 1.1 of 11.1 GB (2.2 MB/sec)
 statusFormat3=%4$S — %1$S (%2$S %3$S/sec)
+
+# LOCALIZATION NOTE (statusFormatNoRate): — is the "em dash" (long dash)
+# %1$S transfer progress; %2$S time left
+# example: 4 minutes left — 1.1 of 11.1 GB
+statusFormatNoRate=%2$S — %1$S
+
 bytes=bytes
 kilobyte=KB
 megabyte=MB
 gigabyte=GB
 
 # LOCALIZATION NOTE (transferSameUnits2):
 # %1$S progress number; %2$S total number; %3$S total unit
 # example: 1.1 of 333 MB
--- a/toolkit/mozapps/downloads/DownloadUtils.jsm
+++ b/toolkit/mozapps/downloads/DownloadUtils.jsm
@@ -50,16 +50,17 @@ const Cu = Components.utils;
   return this.gDecimalSymbol = Number(5.4).toLocaleString().match(/\D/);
 });
 
 const kDownloadProperties =
   "chrome://mozapps/locale/downloads/downloads.properties";
 
 let gStr = {
   statusFormat: "statusFormat3",
+  statusFormatNoRate: "statusFormatNoRate",
   transferSameUnits: "transferSameUnits2",
   transferDiffUnits: "transferDiffUnits2",
   transferNoTotal: "transferNoTotal2",
   timePair: "timePair2",
   timeLeftSingle: "timeLeftSingle2",
   timeLeftDouble: "timeLeftDouble2",
   timeFewSeconds: "timeFewSeconds",
   timeUnknown: "timeUnknown",
@@ -98,35 +99,87 @@ this.DownloadUtils = {
    *        Current transfer rate in bytes/sec or -1 for unknown
    * @param [optional] aLastSec
    *        Last time remaining in seconds or Infinity for unknown
    * @return A pair: [download status text, new value of "last seconds"]
    */
   getDownloadStatus: function DU_getDownloadStatus(aCurrBytes, aMaxBytes,
                                                    aSpeed, aLastSec)
   {
+    let [transfer, timeLeft, newLast, normalizedSpeed]
+      = this._deriveTransferRate(aCurrBytes, aMaxBytes, aSpeed, aLastSec);
+
+    let [rate, unit] = DownloadUtils.convertByteUnits(normalizedSpeed);
+    let params = [transfer, rate, unit, timeLeft];
+    let status = gBundle.formatStringFromName(gStr.statusFormat, params,
+                                              params.length);
+    return [status, newLast];
+  },
+
+  /**
+   * Generate a status string for a download given its current progress,
+   * total size, speed, last time remaining. The status string contains the
+   * time remaining, as well as the total bytes downloaded. Unlike
+   * getDownloadStatus, it does not include the rate of download.
+   *
+   * @param aCurrBytes
+   *        Number of bytes transferred so far
+   * @param [optional] aMaxBytes
+   *        Total number of bytes or -1 for unknown
+   * @param [optional] aSpeed
+   *        Current transfer rate in bytes/sec or -1 for unknown
+   * @param [optional] aLastSec
+   *        Last time remaining in seconds or Infinity for unknown
+   * @return A pair: [download status text, new value of "last seconds"]
+   */
+  getDownloadStatusNoRate:
+  function DU_getDownloadStatusNoRate(aCurrBytes, aMaxBytes, aSpeed,
+                                      aLastSec)
+  {
+    let [transfer, timeLeft, newLast]
+      = this._deriveTransferRate(aCurrBytes, aMaxBytes, aSpeed, aLastSec);
+
+    let params = [transfer, timeLeft];
+    let status = gBundle.formatStringFromName(gStr.statusFormatNoRate, params,
+                                              params.length);
+    return [status, newLast];
+  },
+
+  /**
+   * Helper function that returns a transfer string, a time remaining string,
+   * and a new value of "last seconds".
+   * @param aCurrBytes
+   *        Number of bytes transferred so far
+   * @param [optional] aMaxBytes
+   *        Total number of bytes or -1 for unknown
+   * @param [optional] aSpeed
+   *        Current transfer rate in bytes/sec or -1 for unknown
+   * @param [optional] aLastSec
+   *        Last time remaining in seconds or Infinity for unknown
+   * @return A triple: [amount transferred string, time remaining string,
+   *                    new value of "last seconds"]
+   */
+  _deriveTransferRate: function DU__deriveTransferRate(aCurrBytes,
+                                                       aMaxBytes, aSpeed,
+                                                       aLastSec)
+  {
     if (aMaxBytes == null)
       aMaxBytes = -1;
     if (aSpeed == null)
       aSpeed = -1;
     if (aLastSec == null)
       aLastSec = Infinity;
 
     // Calculate the time remaining if we have valid values
     let seconds = (aSpeed > 0) && (aMaxBytes > 0) ?
       (aMaxBytes - aCurrBytes) / aSpeed : -1;
 
     let transfer = DownloadUtils.getTransferTotal(aCurrBytes, aMaxBytes);
-    let [rate, unit] = DownloadUtils.convertByteUnits(aSpeed);
     let [timeLeft, newLast] = DownloadUtils.getTimeLeft(seconds, aLastSec);
-
-    let params = [transfer, rate, unit, timeLeft];
-    let status = gBundle.formatStringFromName(gStr.statusFormat, params,
-                                              params.length);
-    return [status, newLast];
+    return [transfer, timeLeft, newLast, aSpeed];
   },
 
   /**
    * Generate the transfer progress string to show the current and total byte
    * size. Byte units will be as large as possible and the same units for
    * current and max will be suppressed for the former.
    *
    * @param aCurrBytes
--- a/toolkit/mozapps/downloads/tests/unit/test_DownloadUtils.js
+++ b/toolkit/mozapps/downloads/tests/unit/test_DownloadUtils.js
@@ -23,25 +23,24 @@ function testTransferTotal(aCurrBytes, a
   do_check_eq(transfer, aTransfer);
 }
 
 // Get the em-dash character because typing it directly here doesn't work :(
 let gDash = DownloadUtils.getDownloadStatus(0)[0].match(/remaining (.) 0 bytes/)[1];
 
 let gVals = [0, 100, 2345, 55555, 982341, 23194134, 1482, 58, 9921949201, 13498132];
 
-function testStatus(aCurr, aMore, aRate, aTest)
+function testStatus(aFunc, aCurr, aMore, aRate, aTest)
 {
   dump("Status Test: " + [aCurr, aMore, aRate, aTest] + "\n");
   let curr = gVals[aCurr];
   let max = curr + gVals[aMore];
   let speed = gVals[aRate];
 
-  let [status, last] =
-    DownloadUtils.getDownloadStatus(curr, max, speed);
+  let [status, last] = aFunc(curr, max, speed);
 
   if (0) {
     dump("testStatus(" + aCurr + ", " + aMore + ", " + aRate + ", [\"" +
       status.replace(gDash, "--") + "\", " + last.toFixed(3) + "]);\n");
   }
 
   // Make sure the status text matches
   do_check_eq(status, _(aTest[0].replace(/--/, gDash)));
@@ -130,45 +129,82 @@ function run_test()
   if (0) {
     // Help find some interesting test cases
     let r = function() Math.floor(Math.random() * 10);
     for (let i = 0; i < 100; i++) {
       testStatus(r(), r(), r());
     }
   }
 
-  testStatus(2, 1, 7, ["A few seconds remaining -- 2.3 of 2.4 KB (58 bytes/sec)", 1.724]);
-  testStatus(1, 2, 6, ["A few seconds remaining -- 100 bytes of 2.4 KB (1.4 KB/sec)", 1.582]);
-  testStatus(4, 3, 9, ["A few seconds remaining -- 959 KB of 1.0 MB (12.9 MB/sec)", 0.004]);
-  testStatus(2, 3, 8, ["A few seconds remaining -- 2.3 of 56.5 KB (9.2 GB/sec)", 0.000]);
+  // First, test with rates, via getDownloadStatus...
+  let statusFunc = DownloadUtils.getDownloadStatus.bind(DownloadUtils);
+
+  testStatus(statusFunc, 2, 1, 7, ["A few seconds remaining -- 2.3 of 2.4 KB (58 bytes/sec)", 1.724]);
+  testStatus(statusFunc, 1, 2, 6, ["A few seconds remaining -- 100 bytes of 2.4 KB (1.4 KB/sec)", 1.582]);
+  testStatus(statusFunc, 4, 3, 9, ["A few seconds remaining -- 959 KB of 1.0 MB (12.9 MB/sec)", 0.004]);
+  testStatus(statusFunc, 2, 3, 8, ["A few seconds remaining -- 2.3 of 56.5 KB (9.2 GB/sec)", 0.000]);
+
+  testStatus(statusFunc, 8, 4, 3, ["17 seconds remaining -- 9.2 of 9.2 GB (54.3 KB/sec)", 17.682]);
+  testStatus(statusFunc, 1, 3, 2, ["23 seconds remaining -- 100 bytes of 54.4 KB (2.3 KB/sec)", 23.691]);
+  testStatus(statusFunc, 9, 3, 2, ["23 seconds remaining -- 12.9 of 12.9 MB (2.3 KB/sec)", 23.691]);
+  testStatus(statusFunc, 5, 6, 7, ["25 seconds remaining -- 22.1 of 22.1 MB (58 bytes/sec)", 25.552]);
 
-  testStatus(8, 4, 3, ["17 seconds remaining -- 9.2 of 9.2 GB (54.3 KB/sec)", 17.682]);
-  testStatus(1, 3, 2, ["23 seconds remaining -- 100 bytes of 54.4 KB (2.3 KB/sec)", 23.691]);
-  testStatus(9, 3, 2, ["23 seconds remaining -- 12.9 of 12.9 MB (2.3 KB/sec)", 23.691]);
-  testStatus(5, 6, 7, ["25 seconds remaining -- 22.1 of 22.1 MB (58 bytes/sec)", 25.552]);
+  testStatus(statusFunc, 3, 9, 3, ["4 minutes remaining -- 54.3 KB of 12.9 MB (54.3 KB/sec)", 242.969]);
+  testStatus(statusFunc, 2, 3, 1, ["9 minutes remaining -- 2.3 of 56.5 KB (100 bytes/sec)", 555.550]);
+  testStatus(statusFunc, 4, 3, 7, ["15 minutes remaining -- 959 KB of 1.0 MB (58 bytes/sec)", 957.845]);
+  testStatus(statusFunc, 5, 3, 7, ["15 minutes remaining -- 22.1 of 22.2 MB (58 bytes/sec)", 957.845]);
+
+  testStatus(statusFunc, 1, 9, 2, ["1 hour, 35 minutes remaining -- 100 bytes of 12.9 MB (2.3 KB/sec)", 5756.133]);
+  testStatus(statusFunc, 2, 9, 6, ["2 hours, 31 minutes remaining -- 2.3 KB of 12.9 MB (1.4 KB/sec)", 9108.051]);
+  testStatus(statusFunc, 2, 4, 1, ["2 hours, 43 minutes remaining -- 2.3 of 962 KB (100 bytes/sec)", 9823.410]);
+  testStatus(statusFunc, 6, 4, 7, ["4 hours, 42 minutes remaining -- 1.4 of 961 KB (58 bytes/sec)", 16936.914]);
 
-  testStatus(3, 9, 3, ["4 minutes remaining -- 54.3 KB of 12.9 MB (54.3 KB/sec)", 242.969]);
-  testStatus(2, 3, 1, ["9 minutes remaining -- 2.3 of 56.5 KB (100 bytes/sec)", 555.550]);
-  testStatus(4, 3, 7, ["15 minutes remaining -- 959 KB of 1.0 MB (58 bytes/sec)", 957.845]);
-  testStatus(5, 3, 7, ["15 minutes remaining -- 22.1 of 22.2 MB (58 bytes/sec)", 957.845]);
+  testStatus(statusFunc, 6, 9, 1, ["1 day, 13 hours remaining -- 1.4 KB of 12.9 MB (100 bytes/sec)", 134981.320]);
+  testStatus(statusFunc, 3, 8, 3, ["2 days, 1 hour remaining -- 54.3 KB of 9.2 GB (54.3 KB/sec)", 178596.872]);
+  testStatus(statusFunc, 1, 8, 6, ["77 days, 11 hours remaining -- 100 bytes of 9.2 GB (1.4 KB/sec)", 6694972.470]);
+  testStatus(statusFunc, 6, 8, 7, ["1979 days, 22 hours remaining -- 1.4 KB of 9.2 GB (58 bytes/sec)", 171068089.672]);
+
+  testStatus(statusFunc, 0, 0, 5, ["Unknown time remaining -- 0 of 0 bytes (22.1 MB/sec)", Infinity]);
+  testStatus(statusFunc, 0, 6, 0, ["Unknown time remaining -- 0 bytes of 1.4 KB (0 bytes/sec)", Infinity]);
+  testStatus(statusFunc, 6, 6, 0, ["Unknown time remaining -- 1.4 of 2.9 KB (0 bytes/sec)", Infinity]);
+  testStatus(statusFunc, 8, 5, 0, ["Unknown time remaining -- 9.2 of 9.3 GB (0 bytes/sec)", Infinity]);
 
-  testStatus(1, 9, 2, ["1 hour, 35 minutes remaining -- 100 bytes of 12.9 MB (2.3 KB/sec)", 5756.133]);
-  testStatus(2, 9, 6, ["2 hours, 31 minutes remaining -- 2.3 KB of 12.9 MB (1.4 KB/sec)", 9108.051]);
-  testStatus(2, 4, 1, ["2 hours, 43 minutes remaining -- 2.3 of 962 KB (100 bytes/sec)", 9823.410]);
-  testStatus(6, 4, 7, ["4 hours, 42 minutes remaining -- 1.4 of 961 KB (58 bytes/sec)", 16936.914]);
+
+  // Now test without rates, via getDownloadStatusNoRate.
+  statusFunc = DownloadUtils.getDownloadStatusNoRate.bind(DownloadUtils);
+
+  testStatus(statusFunc, 2, 1, 7, ["A few seconds remaining -- 2.3 of 2.4 KB", 1.724]);
+  testStatus(statusFunc, 1, 2, 6, ["A few seconds remaining -- 100 bytes of 2.4 KB", 1.582]);
+  testStatus(statusFunc, 4, 3, 9, ["A few seconds remaining -- 959 KB of 1.0 MB", 0.004]);
+  testStatus(statusFunc, 2, 3, 8, ["A few seconds remaining -- 2.3 of 56.5 KB", 0.000]);
+
+  testStatus(statusFunc, 8, 4, 3, ["17 seconds remaining -- 9.2 of 9.2 GB", 17.682]);
+  testStatus(statusFunc, 1, 3, 2, ["23 seconds remaining -- 100 bytes of 54.4 KB", 23.691]);
+  testStatus(statusFunc, 9, 3, 2, ["23 seconds remaining -- 12.9 of 12.9 MB", 23.691]);
+  testStatus(statusFunc, 5, 6, 7, ["25 seconds remaining -- 22.1 of 22.1 MB", 25.552]);
 
-  testStatus(6, 9, 1, ["1 day, 13 hours remaining -- 1.4 KB of 12.9 MB (100 bytes/sec)", 134981.320]);
-  testStatus(3, 8, 3, ["2 days, 1 hour remaining -- 54.3 KB of 9.2 GB (54.3 KB/sec)", 178596.872]);
-  testStatus(1, 8, 6, ["77 days, 11 hours remaining -- 100 bytes of 9.2 GB (1.4 KB/sec)", 6694972.470]);
-  testStatus(6, 8, 7, ["1979 days, 22 hours remaining -- 1.4 KB of 9.2 GB (58 bytes/sec)", 171068089.672]);
+  testStatus(statusFunc, 3, 9, 3, ["4 minutes remaining -- 54.3 KB of 12.9 MB", 242.969]);
+  testStatus(statusFunc, 2, 3, 1, ["9 minutes remaining -- 2.3 of 56.5 KB", 555.550]);
+  testStatus(statusFunc, 4, 3, 7, ["15 minutes remaining -- 959 KB of 1.0 MB", 957.845]);
+  testStatus(statusFunc, 5, 3, 7, ["15 minutes remaining -- 22.1 of 22.2 MB", 957.845]);
+
+  testStatus(statusFunc, 1, 9, 2, ["1 hour, 35 minutes remaining -- 100 bytes of 12.9 MB", 5756.133]);
+  testStatus(statusFunc, 2, 9, 6, ["2 hours, 31 minutes remaining -- 2.3 KB of 12.9 MB", 9108.051]);
+  testStatus(statusFunc, 2, 4, 1, ["2 hours, 43 minutes remaining -- 2.3 of 962 KB", 9823.410]);
+  testStatus(statusFunc, 6, 4, 7, ["4 hours, 42 minutes remaining -- 1.4 of 961 KB", 16936.914]);
 
-  testStatus(0, 0, 5, ["Unknown time remaining -- 0 of 0 bytes (22.1 MB/sec)", Infinity]);
-  testStatus(0, 6, 0, ["Unknown time remaining -- 0 bytes of 1.4 KB (0 bytes/sec)", Infinity]);
-  testStatus(6, 6, 0, ["Unknown time remaining -- 1.4 of 2.9 KB (0 bytes/sec)", Infinity]);
-  testStatus(8, 5, 0, ["Unknown time remaining -- 9.2 of 9.3 GB (0 bytes/sec)", Infinity]);
+  testStatus(statusFunc, 6, 9, 1, ["1 day, 13 hours remaining -- 1.4 KB of 12.9 MB", 134981.320]);
+  testStatus(statusFunc, 3, 8, 3, ["2 days, 1 hour remaining -- 54.3 KB of 9.2 GB", 178596.872]);
+  testStatus(statusFunc, 1, 8, 6, ["77 days, 11 hours remaining -- 100 bytes of 9.2 GB", 6694972.470]);
+  testStatus(statusFunc, 6, 8, 7, ["1979 days, 22 hours remaining -- 1.4 KB of 9.2 GB", 171068089.672]);
+
+  testStatus(statusFunc, 0, 0, 5, ["Unknown time remaining -- 0 of 0 bytes", Infinity]);
+  testStatus(statusFunc, 0, 6, 0, ["Unknown time remaining -- 0 bytes of 1.4 KB", Infinity]);
+  testStatus(statusFunc, 6, 6, 0, ["Unknown time remaining -- 1.4 of 2.9 KB", Infinity]);
+  testStatus(statusFunc, 8, 5, 0, ["Unknown time remaining -- 9.2 of 9.3 GB", Infinity]);
 
   testURI("http://www.mozilla.org/", "mozilla.org", "www.mozilla.org");
   testURI("http://www.city.mikasa.hokkaido.jp/", "city.mikasa.hokkaido.jp", "www.city.mikasa.hokkaido.jp");
   testURI("data:text/html,Hello World", "data resource", "data resource");
   testURI("jar:http://www.mozilla.com/file!/magic", "mozilla.com", "www.mozilla.com");
   testURI("file:///C:/Cool/Stuff/", "local file", "local file");
   testURI("moz-icon:file:///test.extension", "moz-icon resource", "moz-icon resource");
   testURI("about:config", "about resource", "about resource");