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 +};