browser/metro/base/content/downloads.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/metro/base/content/downloads.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,656 @@
     1.4 +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png";
    1.10 +
    1.11 +var MetroDownloadsView = {
    1.12 +  /**
    1.13 +   * _downloadCount keeps track of the number of downloads that a single
    1.14 +   * notification bar groups together. A download is grouped with other
    1.15 +   * downloads if it starts before other downloads have completed.
    1.16 +   */
    1.17 +  _downloadCount: 0,
    1.18 +  _downloadsInProgress: 0,
    1.19 +  _lastDownload: null,
    1.20 +  _inited: false,
    1.21 +  _progressAlert: null,
    1.22 +  _lastSec: Infinity,
    1.23 +
    1.24 +  _progressNotificationInfo: new Map(),
    1.25 +  _runDownloadBooleanMap: new Map(),
    1.26 +
    1.27 +  get manager() {
    1.28 +    return Cc["@mozilla.org/download-manager;1"]
    1.29 +             .getService(Ci.nsIDownloadManager);
    1.30 +  },
    1.31 +
    1.32 +  _getReferrerOrSource: function dh__getReferrerOrSource(aDownload) {
    1.33 +    return aDownload.referrer.spec || aDownload.source.spec;
    1.34 +  },
    1.35 +
    1.36 +  _getLocalFile: function dh__getLocalFile(aFileURI) {
    1.37 +    // XXX it's possible that using a null char-set here is bad
    1.38 +    let spec = ('string' == typeof aFileURI) ? aFileURI : aFileURI.spec;
    1.39 +    let fileUrl;
    1.40 +    try {
    1.41 +      fileUrl = Services.io.newURI(spec, null, null).QueryInterface(Ci.nsIFileURL);
    1.42 +    } catch (ex) {
    1.43 +      Util.dumpLn("_getLocalFile: Caught exception creating newURI from file spec: "+aFileURI.spec+": " + ex.message);
    1.44 +      return;
    1.45 +    }
    1.46 +    return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile);
    1.47 +  },
    1.48 +
    1.49 +  init: function dh_init() {
    1.50 +    if (this._inited)
    1.51 +      return;
    1.52 +
    1.53 +    this._inited = true;
    1.54 +
    1.55 +    Services.obs.addObserver(this, "dl-start", true);
    1.56 +    Services.obs.addObserver(this, "dl-done", true);
    1.57 +    Services.obs.addObserver(this, "dl-run", true);
    1.58 +    Services.obs.addObserver(this, "dl-failed", true);
    1.59 +
    1.60 +
    1.61 +    this._progress = new DownloadProgressListener(this);
    1.62 +    this.manager.addListener(this._progress);
    1.63 +
    1.64 +    Elements.tabList.addEventListener("TabClose", this, false);
    1.65 +
    1.66 +    this._downloadProgressIndicator = document.getElementById("download-progress");
    1.67 +
    1.68 +    if (this.manager.activeDownloadCount) {
    1.69 +      setTimeout (this._restartWithActiveDownloads.bind(this), 0);
    1.70 +    }
    1.71 +  },
    1.72 +
    1.73 +  uninit: function dh_uninit() {
    1.74 +    if (this._inited) {
    1.75 +      Services.obs.removeObserver(this, "dl-start");
    1.76 +      Services.obs.removeObserver(this, "dl-done");
    1.77 +      Services.obs.removeObserver(this, "dl-run");
    1.78 +      Services.obs.removeObserver(this, "dl-failed");
    1.79 +      if (Elements && Elements.tabList)
    1.80 +        Elements.tabList.removeEventListener("TabClose", this);
    1.81 +    }
    1.82 +  },
    1.83 +
    1.84 +  get _notificationBox() {
    1.85 +    return Browser.getNotificationBox(Browser.selectedBrowser);
    1.86 +  },
    1.87 +
    1.88 +  get _notificationBoxes() {
    1.89 +    let currentBox = this._notificationBox;
    1.90 +    let boxes = [
    1.91 +      currentBox
    1.92 +    ];
    1.93 +    for (let { linkedBrowser } of Elements.tabList.children) {
    1.94 +      if (linkedBrowser !== Browser.selectedBrowser) {
    1.95 +        let notificationBox = Browser.getNotificationBox(linkedBrowser);
    1.96 +        if (notificationBox)
    1.97 +          boxes.push(notificationBox);
    1.98 +      }
    1.99 +    }
   1.100 +    return boxes;
   1.101 +  },
   1.102 +
   1.103 +  get _progressNotification() {
   1.104 +    let notn = this._getNotificationWithValue("download-progress");
   1.105 +    let currentBox = this._notificationBox;
   1.106 +    // move the progress notification if attached to a different browser
   1.107 +    if (notn && notn.parentNode !== currentBox) {
   1.108 +      notn.parentNode.removeNotification(notn);
   1.109 +      currentBox.insertBefore(notn, currentBox.firstChild);
   1.110 +    }
   1.111 +    return notn;
   1.112 +  },
   1.113 +
   1.114 +  _getNotificationWithValue: function(aValue) {
   1.115 +    let notn;
   1.116 +    let allNotificationBoxes = this._notificationBoxes;
   1.117 +    for(let box of allNotificationBoxes) {
   1.118 +      notn = box.getNotificationWithValue(aValue);
   1.119 +      if (notn) {
   1.120 +        break;
   1.121 +      }
   1.122 +    }
   1.123 +    return notn;
   1.124 +  },
   1.125 +
   1.126 +   _restartWithActiveDownloads: function() {
   1.127 +    let activeDownloads = this.manager.activeDownloads;
   1.128 +
   1.129 +    while (activeDownloads.hasMoreElements()) {
   1.130 +      let dl = activeDownloads.getNext();
   1.131 +      switch (dl.state) {
   1.132 +        case 0: // Downloading
   1.133 +        case 5: // Queued
   1.134 +          this.watchDownload(dl);
   1.135 +          this.updateInfobar();
   1.136 +          break;
   1.137 +      }
   1.138 +    }
   1.139 +    if (this.manager.activeDownloadCount) {
   1.140 +      ContextUI.displayNavbar();
   1.141 +    }
   1.142 +  },
   1.143 +
   1.144 +  openDownload: function dh_openDownload(aDownload) {
   1.145 +    let fileURI = aDownload.target
   1.146 +
   1.147 +    if (!(fileURI && fileURI.spec)) {
   1.148 +      Util.dumpLn("Cant open download "+id+", fileURI is invalid");
   1.149 +      return;
   1.150 +    }
   1.151 +
   1.152 +    let file = this._getLocalFile(fileURI);
   1.153 +    try {
   1.154 +      file && Services.metro.launchInDesktop(aDownload.target.spec, "");
   1.155 +    } catch (ex) {
   1.156 +      Util.dumpLn("Failed to open download, with id: "+id+", download target URI spec: " + fileURI.spec);
   1.157 +      Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
   1.158 +    }
   1.159 +  },
   1.160 +
   1.161 +  removeDownload: function dh_removeDownload(aDownload) {
   1.162 +    // aDownload is the XUL element here,
   1.163 +    // and .target currently returns the target attribute (string value)
   1.164 +    let id = aDownload.getAttribute("downloadId");
   1.165 +    let download = this.manager.getDownload(id);
   1.166 +
   1.167 +    if (download) {
   1.168 +      this.manager.removeDownload(id);
   1.169 +    }
   1.170 +  },
   1.171 +
   1.172 +  cancelDownload: function dh_cancelDownload(aDownload) {
   1.173 +    let fileURI = aDownload.target;
   1.174 +    if (!(fileURI && fileURI.spec)) {
   1.175 +      Util.dumpLn("Cant remove download file for: "+aDownload.id+", fileURI is invalid");
   1.176 +    }
   1.177 +
   1.178 +    try {
   1.179 +      let file = this._getLocalFile(fileURI);
   1.180 +      if (file && file.exists())
   1.181 +        file.remove(false);
   1.182 +      this.manager.cancelDownload(aDownload.id);
   1.183 +
   1.184 +      // If cancelling was successful, stop tracking the download.
   1.185 +      this._progressNotificationInfo.delete(aDownload.guid);
   1.186 +      this._runDownloadBooleanMap.delete(aDownload.targetFile.path);
   1.187 +      this._downloadCount--;
   1.188 +      this._downloadsInProgress--;
   1.189 +      let notn = this._progressNotification;
   1.190 +      if (notn && this._downloadsInProgress <= 0) {
   1.191 +        this._notificationBox.removeNotification(notn);
   1.192 +      }
   1.193 +    } catch (ex) {
   1.194 +      Util.dumpLn("Failed to cancel download, with id: "+aDownload.id+", download target URI spec: " + fileURI.spec);
   1.195 +      Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
   1.196 +    }
   1.197 +  },
   1.198 +
   1.199 +  // Cancels all downloads.
   1.200 +  cancelDownloads: function dh_cancelDownloads() {
   1.201 +    for (let [guid, info] of this._progressNotificationInfo) {
   1.202 +      this.cancelDownload(info.download);
   1.203 +    }
   1.204 +    this._downloadCount = 0;
   1.205 +    this._progressNotificationInfo.clear();
   1.206 +    this._runDownloadBooleanMap.clear();
   1.207 +  },
   1.208 +
   1.209 +  pauseDownload: function dh_pauseDownload(aDownload) {
   1.210 +    let id = aDownload.getAttribute("downloadId");
   1.211 +    this.manager.pauseDownload(id);
   1.212 +  },
   1.213 +
   1.214 +  resumeDownload: function dh_resumeDownload(aDownload) {
   1.215 +    let id = aDownload.getAttribute("downloadId");
   1.216 +    this.manager.resumeDownload(id);
   1.217 +  },
   1.218 +
   1.219 +  showPage: function dh_showPage(aDownload) {
   1.220 +    let id = aDownload.getAttribute("downloadId");
   1.221 +    let download = this.manager.getDownload(id);
   1.222 +    let uri = this._getReferrerOrSource(download);
   1.223 +    if (uri)
   1.224 +      BrowserUI.addAndShowTab(uri, Browser.selectedTab);
   1.225 +  },
   1.226 +
   1.227 +  showAlert: function dh_showAlert(aName, aMessage, aTitle, aObserver) {
   1.228 +    var notifier = Cc["@mozilla.org/alerts-service;1"]
   1.229 +                     .getService(Ci.nsIAlertsService);
   1.230 +
   1.231 +    if (!aTitle)
   1.232 +      aTitle = Strings.browser.GetStringFromName("alertDownloads");
   1.233 +
   1.234 +    notifier.showAlertNotification("", aTitle, aMessage, true, "", aObserver, aName);
   1.235 +  },
   1.236 +
   1.237 +  showNotification: function dh_showNotification(title, msg, buttons, priority) {
   1.238 +    let notification = this._notificationBox.appendNotification(msg,
   1.239 +                                              title,
   1.240 +                                              URI_GENERIC_ICON_DOWNLOAD,
   1.241 +                                              priority,
   1.242 +                                              buttons);
   1.243 +    return notification;
   1.244 +  },
   1.245 +
   1.246 +  _showDownloadFailedNotification: function (aDownload) {
   1.247 +    let tryAgainButtonText =
   1.248 +      Strings.browser.GetStringFromName("downloadTryAgain");
   1.249 +    let cancelButtonText =
   1.250 +      Strings.browser.GetStringFromName("downloadCancel");
   1.251 +
   1.252 +    let message = Strings.browser.formatStringFromName("alertDownloadFailed",
   1.253 +      [aDownload.displayName], 1);
   1.254 +
   1.255 +    let buttons = [
   1.256 +      {
   1.257 +        isDefault: true,
   1.258 +        label: tryAgainButtonText,
   1.259 +        accessKey: "",
   1.260 +        callback: function() {
   1.261 +          MetroDownloadsView.manager.retryDownload(aDownload.id);
   1.262 +        }
   1.263 +      },
   1.264 +      {
   1.265 +        label: cancelButtonText,
   1.266 +        accessKey: "",
   1.267 +        callback: function() {
   1.268 +          MetroDownloadsView.cancelDownload(aDownload);
   1.269 +          MetroDownloadsView._downloadProgressIndicator.reset();
   1.270 +        }
   1.271 +      }
   1.272 +    ];
   1.273 +    this.showNotification("download-failed", message, buttons,
   1.274 +      this._notificationBox.PRIORITY_WARNING_HIGH);
   1.275 +  },
   1.276 +
   1.277 +  _showDownloadCompleteNotification: function () {
   1.278 +    let message = "";
   1.279 +    let showInFilesButtonText = Strings.browser.GetStringFromName("downloadShowInFiles");
   1.280 +
   1.281 +    let buttons = [
   1.282 +      {
   1.283 +        label: showInFilesButtonText,
   1.284 +        accessKey: "",
   1.285 +        callback: function() {
   1.286 +          let fileURI = MetroDownloadsView._lastDownload.target;
   1.287 +          let file = MetroDownloadsView._getLocalFile(fileURI);
   1.288 +          file.reveal();
   1.289 +          MetroDownloadsView._resetCompletedDownloads();
   1.290 +        }
   1.291 +      }
   1.292 +    ];
   1.293 +
   1.294 +    if (this._downloadCount > 1) {
   1.295 +      message = PluralForm.get(this._downloadCount,
   1.296 +                               Strings.browser.GetStringFromName("alertMultipleDownloadsComplete"))
   1.297 +                               .replace("#1", this._downloadCount)
   1.298 +    } else {
   1.299 +      let runButtonText =
   1.300 +        Strings.browser.GetStringFromName("downloadOpen");
   1.301 +      message = Strings.browser.formatStringFromName("alertDownloadsDone2",
   1.302 +        [this._lastDownload.displayName], 1);
   1.303 +
   1.304 +      buttons.unshift({
   1.305 +        isDefault: true,
   1.306 +        label: runButtonText,
   1.307 +        accessKey: "",
   1.308 +        callback: function() {
   1.309 +          MetroDownloadsView.openDownload(MetroDownloadsView._lastDownload);
   1.310 +          MetroDownloadsView._resetCompletedDownloads();
   1.311 +        }
   1.312 +      });
   1.313 +    }
   1.314 +    this._removeNotification("download-complete");
   1.315 +    this.showNotification("download-complete", message, buttons,
   1.316 +      this._notificationBox.PRIORITY_WARNING_MEDIUM);
   1.317 +  },
   1.318 +
   1.319 +  _showDownloadCompleteToast: function () {
   1.320 +    let name = "DownloadComplete";
   1.321 +    let msg = "";
   1.322 +    let title = "";
   1.323 +    let observer = null;
   1.324 +    if (this._downloadCount > 1) {
   1.325 +      title = PluralForm.get(this._downloadCount,
   1.326 +                             Strings.browser.GetStringFromName("alertMultipleDownloadsComplete"))
   1.327 +                             .replace("#1", this._downloadCount)
   1.328 +      msg = PluralForm.get(2, Strings.browser.GetStringFromName("downloadShowInFiles"));
   1.329 +
   1.330 +      observer = {
   1.331 +        observe: function (aSubject, aTopic, aData) {
   1.332 +          switch (aTopic) {
   1.333 +            case "alertclickcallback":
   1.334 +              let fileURI = MetroDownloadsView._lastDownload.target;
   1.335 +              let file = MetroDownloadsView._getLocalFile(fileURI);
   1.336 +              file.reveal();
   1.337 +              MetroDownloadsView._resetCompletedDownloads();
   1.338 +              break;
   1.339 +          }
   1.340 +        }
   1.341 +      }
   1.342 +    } else {
   1.343 +      title = Strings.browser.formatStringFromName("alertDownloadsDone",
   1.344 +        [this._lastDownload.displayName], 1);
   1.345 +      msg = Strings.browser.GetStringFromName("downloadOpenNow");
   1.346 +      observer = {
   1.347 +        observe: function (aSubject, aTopic, aData) {
   1.348 +          switch (aTopic) {
   1.349 +            case "alertclickcallback":
   1.350 +              MetroDownloadsView.openDownload(MetroDownloadsView._lastDownload);
   1.351 +              MetroDownloadsView._resetCompletedDownloads();
   1.352 +              break;
   1.353 +          }
   1.354 +        }
   1.355 +      }
   1.356 +    }
   1.357 +    this.showAlert(name, msg, title, observer);
   1.358 +  },
   1.359 +
   1.360 +  _resetCompletedDownloads: function () {
   1.361 +    this._progressNotificationInfo.clear();
   1.362 +    this._downloadCount = 0;
   1.363 +    this._lastDownload = null;
   1.364 +    this._downloadProgressIndicator.reset();
   1.365 +    this._removeNotification("download-complete");
   1.366 +  },
   1.367 +
   1.368 +  _updateCircularProgressMeter: function dv_updateCircularProgressMeter() {
   1.369 +    if (!this._progressNotificationInfo) {
   1.370 +      return;
   1.371 +    }
   1.372 +
   1.373 +    let totPercent = 0;
   1.374 +    for (let [guid, info] of this._progressNotificationInfo) {
   1.375 +      // info.download => nsIDownload
   1.376 +      totPercent += info.download.percentComplete;
   1.377 +    }
   1.378 +
   1.379 +    let percentComplete = totPercent / this._progressNotificationInfo.size;
   1.380 +    this._downloadProgressIndicator.updateProgress(percentComplete);
   1.381 +  },
   1.382 +
   1.383 +  _computeDownloadProgressString: function dv_computeDownloadProgressString() {
   1.384 +    let totTransferred = 0, totSize = 0, totSecondsLeft = 0;
   1.385 +    let guid, info;
   1.386 +    for ([guid, info] of this._progressNotificationInfo) {
   1.387 +      let size = info.download.size;
   1.388 +      let amountTransferred = info.download.amountTransferred;
   1.389 +      let speed = info.download.speed;
   1.390 +
   1.391 +      totTransferred += amountTransferred;
   1.392 +      totSize += size;
   1.393 +      totSecondsLeft += ((size - amountTransferred) / speed);
   1.394 +    }
   1.395 +
   1.396 +    // Compute progress in bytes.
   1.397 +    let amountTransferred = Util.getDownloadSize(totTransferred);
   1.398 +    let size = Util.getDownloadSize(totSize);
   1.399 +    let progress = amountTransferred + "/" + size;
   1.400 +
   1.401 +    // Compute progress in time.;
   1.402 +    let [timeLeft, newLast] = DownloadUtils.getTimeLeft(totSecondsLeft, this._lastSec);
   1.403 +    this._lastSec = newLast;
   1.404 +
   1.405 +    if (this._downloadCount == 1) {
   1.406 +      return Strings.browser.GetStringFromName("alertDownloadsStart2")
   1.407 +        .replace("#1", info.download.displayName)
   1.408 +        .replace("#2", progress)
   1.409 +        .replace("#3", timeLeft)
   1.410 +    }
   1.411 +
   1.412 +    let numDownloads = this._downloadCount;
   1.413 +    return PluralForm.get(numDownloads,
   1.414 +                          Strings.browser.GetStringFromName("alertDownloadMultiple"))
   1.415 +                          .replace("#1", numDownloads)
   1.416 +                          .replace("#2", progress)
   1.417 +                          .replace("#3", timeLeft);
   1.418 +  },
   1.419 +
   1.420 +  _saveDownloadData: function dv_saveDownloadData(aDownload) {
   1.421 +    if (!this._progressNotificationInfo.get(aDownload.guid)) {
   1.422 +      this._progressNotificationInfo.set(aDownload.guid, {});
   1.423 +    }
   1.424 +    let infoObj = this._progressNotificationInfo.get(aDownload.guid);
   1.425 +    infoObj.download = aDownload;
   1.426 +    this._progressNotificationInfo.set(aDownload.guid, infoObj);
   1.427 +  },
   1.428 +
   1.429 +  onDownloadButton: function dv_onDownloadButton() {
   1.430 +    let progressNotification = this._getNotificationWithValue("download-progress");
   1.431 +    let wasProgressVisible = (progressNotification &&
   1.432 +                              progressNotification.parentNode == this._notificationBox);
   1.433 +    let completeNotification = this._getNotificationWithValue("download-complete");
   1.434 +    let wasCompleteVisible = (completeNotification &&
   1.435 +                              completeNotification.parentNode == this._notificationBox);
   1.436 +
   1.437 +    this._removeNotification("download-complete");
   1.438 +    this._removeNotification("download-progress");
   1.439 +
   1.440 +    if (this._downloadsInProgress && !wasProgressVisible) {
   1.441 +      this.updateInfobar();
   1.442 +    } else if (this._downloadCount && !wasCompleteVisible) {
   1.443 +      this._showDownloadCompleteNotification();
   1.444 +    }
   1.445 +  },
   1.446 +
   1.447 +  _removeNotification: function (aValue) {
   1.448 +    let notification = this._getNotificationWithValue(aValue);
   1.449 +    return notification &&
   1.450 +           notification.parentNode.removeNotification(notification);
   1.451 +  },
   1.452 +
   1.453 +  updateInfobar: function dv_updateInfobar() {
   1.454 +    let message = this._computeDownloadProgressString();
   1.455 +    this._updateCircularProgressMeter();
   1.456 +
   1.457 +    let notn = this._progressNotification;
   1.458 +    if (!notn) {
   1.459 +      let cancelButtonText =
   1.460 +              Strings.browser.GetStringFromName("downloadCancel");
   1.461 +
   1.462 +      let buttons = [
   1.463 +        {
   1.464 +          isDefault: false,
   1.465 +          label: cancelButtonText,
   1.466 +          accessKey: "",
   1.467 +          callback: function() {
   1.468 +            MetroDownloadsView.cancelDownloads();
   1.469 +            MetroDownloadsView._downloadProgressIndicator.reset();
   1.470 +          }
   1.471 +        }
   1.472 +      ];
   1.473 +
   1.474 +      notn = this.showNotification("download-progress", message, buttons,
   1.475 +             this._notificationBox.PRIORITY_WARNING_LOW);
   1.476 +
   1.477 +      ContextUI.displayNavbar();
   1.478 +    } else {
   1.479 +      notn.label = message;
   1.480 +    }
   1.481 +  },
   1.482 +
   1.483 +  updateDownload: function dv_updateDownload(aDownload) {
   1.484 +    this._saveDownloadData(aDownload);
   1.485 +    let notn = this._progressNotification;
   1.486 +    if (notn) {
   1.487 +      notn.label =
   1.488 +        this._computeDownloadProgressString(aDownload);
   1.489 +    }
   1.490 +    this._updateCircularProgressMeter();
   1.491 +  },
   1.492 +
   1.493 +  watchDownload: function dv_watchDownload(aDownload) {
   1.494 +    this._saveDownloadData(aDownload);
   1.495 +    this._downloadCount++;
   1.496 +    this._downloadsInProgress++;
   1.497 +    if (!this._progressNotificationInfo.get(aDownload.guid)) {
   1.498 +      this._progressNotificationInfo.set(aDownload.guid, {});
   1.499 +    }
   1.500 +    if (!this._progressAlert) {
   1.501 +      this._progressAlert = new AlertDownloadProgressListener();
   1.502 +      this.manager.addListener(this._progressAlert);
   1.503 +    }
   1.504 +  },
   1.505 +
   1.506 +  observe: function (aSubject, aTopic, aData) {
   1.507 +    let message = "";
   1.508 +    let msgTitle = "";
   1.509 +
   1.510 +    switch (aTopic) {
   1.511 +      case "dl-run":
   1.512 +        let file = aSubject.QueryInterface(Ci.nsIFile);
   1.513 +        this._runDownloadBooleanMap.set(file.path, (aData == 'true'));
   1.514 +        break;
   1.515 +      case "dl-start":
   1.516 +        let download = aSubject.QueryInterface(Ci.nsIDownload);
   1.517 +        this.watchDownload(download);
   1.518 +        this.updateInfobar();
   1.519 +        break;
   1.520 +      case "dl-done":
   1.521 +        this._downloadsInProgress--;
   1.522 +        download = aSubject.QueryInterface(Ci.nsIDownload);
   1.523 +        this._lastDownload = download;
   1.524 +        let runAfterDownload = this._runDownloadBooleanMap.get(download.targetFile.path);
   1.525 +        if (runAfterDownload) {
   1.526 +          this.openDownload(download);
   1.527 +        }
   1.528 +
   1.529 +        this._runDownloadBooleanMap.delete(download.targetFile.path);
   1.530 +        if (this._downloadsInProgress == 0) {
   1.531 +          if (this._downloadCount > 1 || !runAfterDownload) {
   1.532 +            this._showDownloadCompleteToast();
   1.533 +            this._showDownloadCompleteNotification();
   1.534 +          }
   1.535 +          let notn = this._progressNotification;
   1.536 +          if (notn)
   1.537 +            this._notificationBox.removeNotification(notn);
   1.538 +
   1.539 +          ContextUI.displayNavbar();
   1.540 +        }
   1.541 +
   1.542 +        this._downloadProgressIndicator.notify();
   1.543 +        break;
   1.544 +      case "dl-failed":
   1.545 +        download = aSubject.QueryInterface(Ci.nsIDownload);
   1.546 +        this._showDownloadFailedNotification(download);
   1.547 +        break;
   1.548 +    }
   1.549 +  },
   1.550 +
   1.551 +  handleEvent: function(aEvent) {
   1.552 +    switch (aEvent.type) {
   1.553 +      case 'TabClose': {
   1.554 +        let browser = aEvent.originalTarget.linkedBrowser;
   1.555 +        let tab = Browser.getTabForBrowser(browser);
   1.556 +        let notificationBox = Browser.getNotificationBox(browser);
   1.557 +
   1.558 +        // move any download-related notification before the tab and its notificationBox goes away
   1.559 +        // The 3 possible values should be mutually exclusive
   1.560 +        for(let name of ["download-progress",
   1.561 +                        "save-download",
   1.562 +                        "download-complete"]) {
   1.563 +          let notn = notificationBox.getNotificationWithValue(name);
   1.564 +          if (!notn) {
   1.565 +            continue;
   1.566 +          }
   1.567 +
   1.568 +          let nextTab = Browser.getNextTab(tab);
   1.569 +          let nextBox = nextTab && Browser.getNotificationBox(nextTab.browser);
   1.570 +          if (nextBox) {
   1.571 +            // move notification to the next tab
   1.572 +            nextBox.adoptNotification(notn);
   1.573 +          } else {
   1.574 +            // Alas, no browser to move the notifications to.
   1.575 +          }
   1.576 +        }
   1.577 +        break;
   1.578 +      }
   1.579 +    }
   1.580 +  },
   1.581 +
   1.582 +  QueryInterface: function (aIID) {
   1.583 +    if (!aIID.equals(Ci.nsIObserver) &&
   1.584 +        !aIID.equals(Ci.nsISupportsWeakReference) &&
   1.585 +        !aIID.equals(Ci.nsISupports))
   1.586 +      throw Components.results.NS_ERROR_NO_INTERFACE;
   1.587 +    return this;
   1.588 +  }
   1.589 +};
   1.590 +
   1.591 +
   1.592 +/**
   1.593 + * Notifies Downloads object about updates in the state of various downloads.
   1.594 + *
   1.595 + * @param aDownloads An instance of Downloads.
   1.596 + */
   1.597 +function DownloadProgressListener(aDownloads) {
   1.598 +  this._downloads = aDownloads;
   1.599 +}
   1.600 +
   1.601 +DownloadProgressListener.prototype = {
   1.602 +  _downloads: null,
   1.603 +
   1.604 +  //////////////////////////////////////////////////////////////////////////////
   1.605 +  //// nsIDownloadProgressListener
   1.606 +  onDownloadStateChange: function dPL_onDownloadStateChange(aState, aDownload) {
   1.607 +    // TODO: Use DownloadProgressListener instead of observers in the Downloads object.
   1.608 +    this._downloads.updateDownload(aDownload);
   1.609 +  },
   1.610 +
   1.611 +  onProgressChange: function dPL_onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) {
   1.612 +    // TODO <jwilde>: Add more detailed progress information.
   1.613 +    this._downloads.updateDownload(aDownload);
   1.614 +  },
   1.615 +
   1.616 +  onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { },
   1.617 +  onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { },
   1.618 +
   1.619 +  //////////////////////////////////////////////////////////////////////////////
   1.620 +  //// nsISupports
   1.621 +  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener])
   1.622 +};
   1.623 +
   1.624 +
   1.625 +/**
   1.626 + * Tracks download progress so that additional information can be displayed
   1.627 + * about its download in alert popups.
   1.628 + */
   1.629 +function AlertDownloadProgressListener() { }
   1.630 +
   1.631 +AlertDownloadProgressListener.prototype = {
   1.632 +  //////////////////////////////////////////////////////////////////////////////
   1.633 +  //// nsIDownloadProgressListener
   1.634 +  onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) {
   1.635 +    let strings = Strings.browser;
   1.636 +    let availableSpace = -1;
   1.637 +
   1.638 +    try {
   1.639 +      // diskSpaceAvailable is not implemented on all systems
   1.640 +      let availableSpace = aDownload.targetFile.diskSpaceAvailable;
   1.641 +    } catch(ex) { }
   1.642 +
   1.643 +    let contentLength = aDownload.size;
   1.644 +    if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
   1.645 +      MetroDownloadsView.showAlert(aDownload.target.spec.replace("file:", "download:"),
   1.646 +                                   strings.GetStringFromName("alertDownloadsNoSpace"),
   1.647 +                                   strings.GetStringFromName("alertDownloadsSize"));
   1.648 +      MetroDownloadsView.cancelDownload(aDownload);
   1.649 +    }
   1.650 +  },
   1.651 +
   1.652 +  onDownloadStateChange: function(aState, aDownload) { },
   1.653 +  onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { },
   1.654 +  onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { },
   1.655 +
   1.656 +  //////////////////////////////////////////////////////////////////////////////
   1.657 +  //// nsISupports
   1.658 +  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener])
   1.659 +};

mercurial