|
1 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png"; |
|
7 |
|
8 var MetroDownloadsView = { |
|
9 /** |
|
10 * _downloadCount keeps track of the number of downloads that a single |
|
11 * notification bar groups together. A download is grouped with other |
|
12 * downloads if it starts before other downloads have completed. |
|
13 */ |
|
14 _downloadCount: 0, |
|
15 _downloadsInProgress: 0, |
|
16 _lastDownload: null, |
|
17 _inited: false, |
|
18 _progressAlert: null, |
|
19 _lastSec: Infinity, |
|
20 |
|
21 _progressNotificationInfo: new Map(), |
|
22 _runDownloadBooleanMap: new Map(), |
|
23 |
|
24 get manager() { |
|
25 return Cc["@mozilla.org/download-manager;1"] |
|
26 .getService(Ci.nsIDownloadManager); |
|
27 }, |
|
28 |
|
29 _getReferrerOrSource: function dh__getReferrerOrSource(aDownload) { |
|
30 return aDownload.referrer.spec || aDownload.source.spec; |
|
31 }, |
|
32 |
|
33 _getLocalFile: function dh__getLocalFile(aFileURI) { |
|
34 // XXX it's possible that using a null char-set here is bad |
|
35 let spec = ('string' == typeof aFileURI) ? aFileURI : aFileURI.spec; |
|
36 let fileUrl; |
|
37 try { |
|
38 fileUrl = Services.io.newURI(spec, null, null).QueryInterface(Ci.nsIFileURL); |
|
39 } catch (ex) { |
|
40 Util.dumpLn("_getLocalFile: Caught exception creating newURI from file spec: "+aFileURI.spec+": " + ex.message); |
|
41 return; |
|
42 } |
|
43 return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile); |
|
44 }, |
|
45 |
|
46 init: function dh_init() { |
|
47 if (this._inited) |
|
48 return; |
|
49 |
|
50 this._inited = true; |
|
51 |
|
52 Services.obs.addObserver(this, "dl-start", true); |
|
53 Services.obs.addObserver(this, "dl-done", true); |
|
54 Services.obs.addObserver(this, "dl-run", true); |
|
55 Services.obs.addObserver(this, "dl-failed", true); |
|
56 |
|
57 |
|
58 this._progress = new DownloadProgressListener(this); |
|
59 this.manager.addListener(this._progress); |
|
60 |
|
61 Elements.tabList.addEventListener("TabClose", this, false); |
|
62 |
|
63 this._downloadProgressIndicator = document.getElementById("download-progress"); |
|
64 |
|
65 if (this.manager.activeDownloadCount) { |
|
66 setTimeout (this._restartWithActiveDownloads.bind(this), 0); |
|
67 } |
|
68 }, |
|
69 |
|
70 uninit: function dh_uninit() { |
|
71 if (this._inited) { |
|
72 Services.obs.removeObserver(this, "dl-start"); |
|
73 Services.obs.removeObserver(this, "dl-done"); |
|
74 Services.obs.removeObserver(this, "dl-run"); |
|
75 Services.obs.removeObserver(this, "dl-failed"); |
|
76 if (Elements && Elements.tabList) |
|
77 Elements.tabList.removeEventListener("TabClose", this); |
|
78 } |
|
79 }, |
|
80 |
|
81 get _notificationBox() { |
|
82 return Browser.getNotificationBox(Browser.selectedBrowser); |
|
83 }, |
|
84 |
|
85 get _notificationBoxes() { |
|
86 let currentBox = this._notificationBox; |
|
87 let boxes = [ |
|
88 currentBox |
|
89 ]; |
|
90 for (let { linkedBrowser } of Elements.tabList.children) { |
|
91 if (linkedBrowser !== Browser.selectedBrowser) { |
|
92 let notificationBox = Browser.getNotificationBox(linkedBrowser); |
|
93 if (notificationBox) |
|
94 boxes.push(notificationBox); |
|
95 } |
|
96 } |
|
97 return boxes; |
|
98 }, |
|
99 |
|
100 get _progressNotification() { |
|
101 let notn = this._getNotificationWithValue("download-progress"); |
|
102 let currentBox = this._notificationBox; |
|
103 // move the progress notification if attached to a different browser |
|
104 if (notn && notn.parentNode !== currentBox) { |
|
105 notn.parentNode.removeNotification(notn); |
|
106 currentBox.insertBefore(notn, currentBox.firstChild); |
|
107 } |
|
108 return notn; |
|
109 }, |
|
110 |
|
111 _getNotificationWithValue: function(aValue) { |
|
112 let notn; |
|
113 let allNotificationBoxes = this._notificationBoxes; |
|
114 for(let box of allNotificationBoxes) { |
|
115 notn = box.getNotificationWithValue(aValue); |
|
116 if (notn) { |
|
117 break; |
|
118 } |
|
119 } |
|
120 return notn; |
|
121 }, |
|
122 |
|
123 _restartWithActiveDownloads: function() { |
|
124 let activeDownloads = this.manager.activeDownloads; |
|
125 |
|
126 while (activeDownloads.hasMoreElements()) { |
|
127 let dl = activeDownloads.getNext(); |
|
128 switch (dl.state) { |
|
129 case 0: // Downloading |
|
130 case 5: // Queued |
|
131 this.watchDownload(dl); |
|
132 this.updateInfobar(); |
|
133 break; |
|
134 } |
|
135 } |
|
136 if (this.manager.activeDownloadCount) { |
|
137 ContextUI.displayNavbar(); |
|
138 } |
|
139 }, |
|
140 |
|
141 openDownload: function dh_openDownload(aDownload) { |
|
142 let fileURI = aDownload.target |
|
143 |
|
144 if (!(fileURI && fileURI.spec)) { |
|
145 Util.dumpLn("Cant open download "+id+", fileURI is invalid"); |
|
146 return; |
|
147 } |
|
148 |
|
149 let file = this._getLocalFile(fileURI); |
|
150 try { |
|
151 file && Services.metro.launchInDesktop(aDownload.target.spec, ""); |
|
152 } catch (ex) { |
|
153 Util.dumpLn("Failed to open download, with id: "+id+", download target URI spec: " + fileURI.spec); |
|
154 Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec)); |
|
155 } |
|
156 }, |
|
157 |
|
158 removeDownload: function dh_removeDownload(aDownload) { |
|
159 // aDownload is the XUL element here, |
|
160 // and .target currently returns the target attribute (string value) |
|
161 let id = aDownload.getAttribute("downloadId"); |
|
162 let download = this.manager.getDownload(id); |
|
163 |
|
164 if (download) { |
|
165 this.manager.removeDownload(id); |
|
166 } |
|
167 }, |
|
168 |
|
169 cancelDownload: function dh_cancelDownload(aDownload) { |
|
170 let fileURI = aDownload.target; |
|
171 if (!(fileURI && fileURI.spec)) { |
|
172 Util.dumpLn("Cant remove download file for: "+aDownload.id+", fileURI is invalid"); |
|
173 } |
|
174 |
|
175 try { |
|
176 let file = this._getLocalFile(fileURI); |
|
177 if (file && file.exists()) |
|
178 file.remove(false); |
|
179 this.manager.cancelDownload(aDownload.id); |
|
180 |
|
181 // If cancelling was successful, stop tracking the download. |
|
182 this._progressNotificationInfo.delete(aDownload.guid); |
|
183 this._runDownloadBooleanMap.delete(aDownload.targetFile.path); |
|
184 this._downloadCount--; |
|
185 this._downloadsInProgress--; |
|
186 let notn = this._progressNotification; |
|
187 if (notn && this._downloadsInProgress <= 0) { |
|
188 this._notificationBox.removeNotification(notn); |
|
189 } |
|
190 } catch (ex) { |
|
191 Util.dumpLn("Failed to cancel download, with id: "+aDownload.id+", download target URI spec: " + fileURI.spec); |
|
192 Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec)); |
|
193 } |
|
194 }, |
|
195 |
|
196 // Cancels all downloads. |
|
197 cancelDownloads: function dh_cancelDownloads() { |
|
198 for (let [guid, info] of this._progressNotificationInfo) { |
|
199 this.cancelDownload(info.download); |
|
200 } |
|
201 this._downloadCount = 0; |
|
202 this._progressNotificationInfo.clear(); |
|
203 this._runDownloadBooleanMap.clear(); |
|
204 }, |
|
205 |
|
206 pauseDownload: function dh_pauseDownload(aDownload) { |
|
207 let id = aDownload.getAttribute("downloadId"); |
|
208 this.manager.pauseDownload(id); |
|
209 }, |
|
210 |
|
211 resumeDownload: function dh_resumeDownload(aDownload) { |
|
212 let id = aDownload.getAttribute("downloadId"); |
|
213 this.manager.resumeDownload(id); |
|
214 }, |
|
215 |
|
216 showPage: function dh_showPage(aDownload) { |
|
217 let id = aDownload.getAttribute("downloadId"); |
|
218 let download = this.manager.getDownload(id); |
|
219 let uri = this._getReferrerOrSource(download); |
|
220 if (uri) |
|
221 BrowserUI.addAndShowTab(uri, Browser.selectedTab); |
|
222 }, |
|
223 |
|
224 showAlert: function dh_showAlert(aName, aMessage, aTitle, aObserver) { |
|
225 var notifier = Cc["@mozilla.org/alerts-service;1"] |
|
226 .getService(Ci.nsIAlertsService); |
|
227 |
|
228 if (!aTitle) |
|
229 aTitle = Strings.browser.GetStringFromName("alertDownloads"); |
|
230 |
|
231 notifier.showAlertNotification("", aTitle, aMessage, true, "", aObserver, aName); |
|
232 }, |
|
233 |
|
234 showNotification: function dh_showNotification(title, msg, buttons, priority) { |
|
235 let notification = this._notificationBox.appendNotification(msg, |
|
236 title, |
|
237 URI_GENERIC_ICON_DOWNLOAD, |
|
238 priority, |
|
239 buttons); |
|
240 return notification; |
|
241 }, |
|
242 |
|
243 _showDownloadFailedNotification: function (aDownload) { |
|
244 let tryAgainButtonText = |
|
245 Strings.browser.GetStringFromName("downloadTryAgain"); |
|
246 let cancelButtonText = |
|
247 Strings.browser.GetStringFromName("downloadCancel"); |
|
248 |
|
249 let message = Strings.browser.formatStringFromName("alertDownloadFailed", |
|
250 [aDownload.displayName], 1); |
|
251 |
|
252 let buttons = [ |
|
253 { |
|
254 isDefault: true, |
|
255 label: tryAgainButtonText, |
|
256 accessKey: "", |
|
257 callback: function() { |
|
258 MetroDownloadsView.manager.retryDownload(aDownload.id); |
|
259 } |
|
260 }, |
|
261 { |
|
262 label: cancelButtonText, |
|
263 accessKey: "", |
|
264 callback: function() { |
|
265 MetroDownloadsView.cancelDownload(aDownload); |
|
266 MetroDownloadsView._downloadProgressIndicator.reset(); |
|
267 } |
|
268 } |
|
269 ]; |
|
270 this.showNotification("download-failed", message, buttons, |
|
271 this._notificationBox.PRIORITY_WARNING_HIGH); |
|
272 }, |
|
273 |
|
274 _showDownloadCompleteNotification: function () { |
|
275 let message = ""; |
|
276 let showInFilesButtonText = Strings.browser.GetStringFromName("downloadShowInFiles"); |
|
277 |
|
278 let buttons = [ |
|
279 { |
|
280 label: showInFilesButtonText, |
|
281 accessKey: "", |
|
282 callback: function() { |
|
283 let fileURI = MetroDownloadsView._lastDownload.target; |
|
284 let file = MetroDownloadsView._getLocalFile(fileURI); |
|
285 file.reveal(); |
|
286 MetroDownloadsView._resetCompletedDownloads(); |
|
287 } |
|
288 } |
|
289 ]; |
|
290 |
|
291 if (this._downloadCount > 1) { |
|
292 message = PluralForm.get(this._downloadCount, |
|
293 Strings.browser.GetStringFromName("alertMultipleDownloadsComplete")) |
|
294 .replace("#1", this._downloadCount) |
|
295 } else { |
|
296 let runButtonText = |
|
297 Strings.browser.GetStringFromName("downloadOpen"); |
|
298 message = Strings.browser.formatStringFromName("alertDownloadsDone2", |
|
299 [this._lastDownload.displayName], 1); |
|
300 |
|
301 buttons.unshift({ |
|
302 isDefault: true, |
|
303 label: runButtonText, |
|
304 accessKey: "", |
|
305 callback: function() { |
|
306 MetroDownloadsView.openDownload(MetroDownloadsView._lastDownload); |
|
307 MetroDownloadsView._resetCompletedDownloads(); |
|
308 } |
|
309 }); |
|
310 } |
|
311 this._removeNotification("download-complete"); |
|
312 this.showNotification("download-complete", message, buttons, |
|
313 this._notificationBox.PRIORITY_WARNING_MEDIUM); |
|
314 }, |
|
315 |
|
316 _showDownloadCompleteToast: function () { |
|
317 let name = "DownloadComplete"; |
|
318 let msg = ""; |
|
319 let title = ""; |
|
320 let observer = null; |
|
321 if (this._downloadCount > 1) { |
|
322 title = PluralForm.get(this._downloadCount, |
|
323 Strings.browser.GetStringFromName("alertMultipleDownloadsComplete")) |
|
324 .replace("#1", this._downloadCount) |
|
325 msg = PluralForm.get(2, Strings.browser.GetStringFromName("downloadShowInFiles")); |
|
326 |
|
327 observer = { |
|
328 observe: function (aSubject, aTopic, aData) { |
|
329 switch (aTopic) { |
|
330 case "alertclickcallback": |
|
331 let fileURI = MetroDownloadsView._lastDownload.target; |
|
332 let file = MetroDownloadsView._getLocalFile(fileURI); |
|
333 file.reveal(); |
|
334 MetroDownloadsView._resetCompletedDownloads(); |
|
335 break; |
|
336 } |
|
337 } |
|
338 } |
|
339 } else { |
|
340 title = Strings.browser.formatStringFromName("alertDownloadsDone", |
|
341 [this._lastDownload.displayName], 1); |
|
342 msg = Strings.browser.GetStringFromName("downloadOpenNow"); |
|
343 observer = { |
|
344 observe: function (aSubject, aTopic, aData) { |
|
345 switch (aTopic) { |
|
346 case "alertclickcallback": |
|
347 MetroDownloadsView.openDownload(MetroDownloadsView._lastDownload); |
|
348 MetroDownloadsView._resetCompletedDownloads(); |
|
349 break; |
|
350 } |
|
351 } |
|
352 } |
|
353 } |
|
354 this.showAlert(name, msg, title, observer); |
|
355 }, |
|
356 |
|
357 _resetCompletedDownloads: function () { |
|
358 this._progressNotificationInfo.clear(); |
|
359 this._downloadCount = 0; |
|
360 this._lastDownload = null; |
|
361 this._downloadProgressIndicator.reset(); |
|
362 this._removeNotification("download-complete"); |
|
363 }, |
|
364 |
|
365 _updateCircularProgressMeter: function dv_updateCircularProgressMeter() { |
|
366 if (!this._progressNotificationInfo) { |
|
367 return; |
|
368 } |
|
369 |
|
370 let totPercent = 0; |
|
371 for (let [guid, info] of this._progressNotificationInfo) { |
|
372 // info.download => nsIDownload |
|
373 totPercent += info.download.percentComplete; |
|
374 } |
|
375 |
|
376 let percentComplete = totPercent / this._progressNotificationInfo.size; |
|
377 this._downloadProgressIndicator.updateProgress(percentComplete); |
|
378 }, |
|
379 |
|
380 _computeDownloadProgressString: function dv_computeDownloadProgressString() { |
|
381 let totTransferred = 0, totSize = 0, totSecondsLeft = 0; |
|
382 let guid, info; |
|
383 for ([guid, info] of this._progressNotificationInfo) { |
|
384 let size = info.download.size; |
|
385 let amountTransferred = info.download.amountTransferred; |
|
386 let speed = info.download.speed; |
|
387 |
|
388 totTransferred += amountTransferred; |
|
389 totSize += size; |
|
390 totSecondsLeft += ((size - amountTransferred) / speed); |
|
391 } |
|
392 |
|
393 // Compute progress in bytes. |
|
394 let amountTransferred = Util.getDownloadSize(totTransferred); |
|
395 let size = Util.getDownloadSize(totSize); |
|
396 let progress = amountTransferred + "/" + size; |
|
397 |
|
398 // Compute progress in time.; |
|
399 let [timeLeft, newLast] = DownloadUtils.getTimeLeft(totSecondsLeft, this._lastSec); |
|
400 this._lastSec = newLast; |
|
401 |
|
402 if (this._downloadCount == 1) { |
|
403 return Strings.browser.GetStringFromName("alertDownloadsStart2") |
|
404 .replace("#1", info.download.displayName) |
|
405 .replace("#2", progress) |
|
406 .replace("#3", timeLeft) |
|
407 } |
|
408 |
|
409 let numDownloads = this._downloadCount; |
|
410 return PluralForm.get(numDownloads, |
|
411 Strings.browser.GetStringFromName("alertDownloadMultiple")) |
|
412 .replace("#1", numDownloads) |
|
413 .replace("#2", progress) |
|
414 .replace("#3", timeLeft); |
|
415 }, |
|
416 |
|
417 _saveDownloadData: function dv_saveDownloadData(aDownload) { |
|
418 if (!this._progressNotificationInfo.get(aDownload.guid)) { |
|
419 this._progressNotificationInfo.set(aDownload.guid, {}); |
|
420 } |
|
421 let infoObj = this._progressNotificationInfo.get(aDownload.guid); |
|
422 infoObj.download = aDownload; |
|
423 this._progressNotificationInfo.set(aDownload.guid, infoObj); |
|
424 }, |
|
425 |
|
426 onDownloadButton: function dv_onDownloadButton() { |
|
427 let progressNotification = this._getNotificationWithValue("download-progress"); |
|
428 let wasProgressVisible = (progressNotification && |
|
429 progressNotification.parentNode == this._notificationBox); |
|
430 let completeNotification = this._getNotificationWithValue("download-complete"); |
|
431 let wasCompleteVisible = (completeNotification && |
|
432 completeNotification.parentNode == this._notificationBox); |
|
433 |
|
434 this._removeNotification("download-complete"); |
|
435 this._removeNotification("download-progress"); |
|
436 |
|
437 if (this._downloadsInProgress && !wasProgressVisible) { |
|
438 this.updateInfobar(); |
|
439 } else if (this._downloadCount && !wasCompleteVisible) { |
|
440 this._showDownloadCompleteNotification(); |
|
441 } |
|
442 }, |
|
443 |
|
444 _removeNotification: function (aValue) { |
|
445 let notification = this._getNotificationWithValue(aValue); |
|
446 return notification && |
|
447 notification.parentNode.removeNotification(notification); |
|
448 }, |
|
449 |
|
450 updateInfobar: function dv_updateInfobar() { |
|
451 let message = this._computeDownloadProgressString(); |
|
452 this._updateCircularProgressMeter(); |
|
453 |
|
454 let notn = this._progressNotification; |
|
455 if (!notn) { |
|
456 let cancelButtonText = |
|
457 Strings.browser.GetStringFromName("downloadCancel"); |
|
458 |
|
459 let buttons = [ |
|
460 { |
|
461 isDefault: false, |
|
462 label: cancelButtonText, |
|
463 accessKey: "", |
|
464 callback: function() { |
|
465 MetroDownloadsView.cancelDownloads(); |
|
466 MetroDownloadsView._downloadProgressIndicator.reset(); |
|
467 } |
|
468 } |
|
469 ]; |
|
470 |
|
471 notn = this.showNotification("download-progress", message, buttons, |
|
472 this._notificationBox.PRIORITY_WARNING_LOW); |
|
473 |
|
474 ContextUI.displayNavbar(); |
|
475 } else { |
|
476 notn.label = message; |
|
477 } |
|
478 }, |
|
479 |
|
480 updateDownload: function dv_updateDownload(aDownload) { |
|
481 this._saveDownloadData(aDownload); |
|
482 let notn = this._progressNotification; |
|
483 if (notn) { |
|
484 notn.label = |
|
485 this._computeDownloadProgressString(aDownload); |
|
486 } |
|
487 this._updateCircularProgressMeter(); |
|
488 }, |
|
489 |
|
490 watchDownload: function dv_watchDownload(aDownload) { |
|
491 this._saveDownloadData(aDownload); |
|
492 this._downloadCount++; |
|
493 this._downloadsInProgress++; |
|
494 if (!this._progressNotificationInfo.get(aDownload.guid)) { |
|
495 this._progressNotificationInfo.set(aDownload.guid, {}); |
|
496 } |
|
497 if (!this._progressAlert) { |
|
498 this._progressAlert = new AlertDownloadProgressListener(); |
|
499 this.manager.addListener(this._progressAlert); |
|
500 } |
|
501 }, |
|
502 |
|
503 observe: function (aSubject, aTopic, aData) { |
|
504 let message = ""; |
|
505 let msgTitle = ""; |
|
506 |
|
507 switch (aTopic) { |
|
508 case "dl-run": |
|
509 let file = aSubject.QueryInterface(Ci.nsIFile); |
|
510 this._runDownloadBooleanMap.set(file.path, (aData == 'true')); |
|
511 break; |
|
512 case "dl-start": |
|
513 let download = aSubject.QueryInterface(Ci.nsIDownload); |
|
514 this.watchDownload(download); |
|
515 this.updateInfobar(); |
|
516 break; |
|
517 case "dl-done": |
|
518 this._downloadsInProgress--; |
|
519 download = aSubject.QueryInterface(Ci.nsIDownload); |
|
520 this._lastDownload = download; |
|
521 let runAfterDownload = this._runDownloadBooleanMap.get(download.targetFile.path); |
|
522 if (runAfterDownload) { |
|
523 this.openDownload(download); |
|
524 } |
|
525 |
|
526 this._runDownloadBooleanMap.delete(download.targetFile.path); |
|
527 if (this._downloadsInProgress == 0) { |
|
528 if (this._downloadCount > 1 || !runAfterDownload) { |
|
529 this._showDownloadCompleteToast(); |
|
530 this._showDownloadCompleteNotification(); |
|
531 } |
|
532 let notn = this._progressNotification; |
|
533 if (notn) |
|
534 this._notificationBox.removeNotification(notn); |
|
535 |
|
536 ContextUI.displayNavbar(); |
|
537 } |
|
538 |
|
539 this._downloadProgressIndicator.notify(); |
|
540 break; |
|
541 case "dl-failed": |
|
542 download = aSubject.QueryInterface(Ci.nsIDownload); |
|
543 this._showDownloadFailedNotification(download); |
|
544 break; |
|
545 } |
|
546 }, |
|
547 |
|
548 handleEvent: function(aEvent) { |
|
549 switch (aEvent.type) { |
|
550 case 'TabClose': { |
|
551 let browser = aEvent.originalTarget.linkedBrowser; |
|
552 let tab = Browser.getTabForBrowser(browser); |
|
553 let notificationBox = Browser.getNotificationBox(browser); |
|
554 |
|
555 // move any download-related notification before the tab and its notificationBox goes away |
|
556 // The 3 possible values should be mutually exclusive |
|
557 for(let name of ["download-progress", |
|
558 "save-download", |
|
559 "download-complete"]) { |
|
560 let notn = notificationBox.getNotificationWithValue(name); |
|
561 if (!notn) { |
|
562 continue; |
|
563 } |
|
564 |
|
565 let nextTab = Browser.getNextTab(tab); |
|
566 let nextBox = nextTab && Browser.getNotificationBox(nextTab.browser); |
|
567 if (nextBox) { |
|
568 // move notification to the next tab |
|
569 nextBox.adoptNotification(notn); |
|
570 } else { |
|
571 // Alas, no browser to move the notifications to. |
|
572 } |
|
573 } |
|
574 break; |
|
575 } |
|
576 } |
|
577 }, |
|
578 |
|
579 QueryInterface: function (aIID) { |
|
580 if (!aIID.equals(Ci.nsIObserver) && |
|
581 !aIID.equals(Ci.nsISupportsWeakReference) && |
|
582 !aIID.equals(Ci.nsISupports)) |
|
583 throw Components.results.NS_ERROR_NO_INTERFACE; |
|
584 return this; |
|
585 } |
|
586 }; |
|
587 |
|
588 |
|
589 /** |
|
590 * Notifies Downloads object about updates in the state of various downloads. |
|
591 * |
|
592 * @param aDownloads An instance of Downloads. |
|
593 */ |
|
594 function DownloadProgressListener(aDownloads) { |
|
595 this._downloads = aDownloads; |
|
596 } |
|
597 |
|
598 DownloadProgressListener.prototype = { |
|
599 _downloads: null, |
|
600 |
|
601 ////////////////////////////////////////////////////////////////////////////// |
|
602 //// nsIDownloadProgressListener |
|
603 onDownloadStateChange: function dPL_onDownloadStateChange(aState, aDownload) { |
|
604 // TODO: Use DownloadProgressListener instead of observers in the Downloads object. |
|
605 this._downloads.updateDownload(aDownload); |
|
606 }, |
|
607 |
|
608 onProgressChange: function dPL_onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) { |
|
609 // TODO <jwilde>: Add more detailed progress information. |
|
610 this._downloads.updateDownload(aDownload); |
|
611 }, |
|
612 |
|
613 onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { }, |
|
614 onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { }, |
|
615 |
|
616 ////////////////////////////////////////////////////////////////////////////// |
|
617 //// nsISupports |
|
618 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener]) |
|
619 }; |
|
620 |
|
621 |
|
622 /** |
|
623 * Tracks download progress so that additional information can be displayed |
|
624 * about its download in alert popups. |
|
625 */ |
|
626 function AlertDownloadProgressListener() { } |
|
627 |
|
628 AlertDownloadProgressListener.prototype = { |
|
629 ////////////////////////////////////////////////////////////////////////////// |
|
630 //// nsIDownloadProgressListener |
|
631 onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) { |
|
632 let strings = Strings.browser; |
|
633 let availableSpace = -1; |
|
634 |
|
635 try { |
|
636 // diskSpaceAvailable is not implemented on all systems |
|
637 let availableSpace = aDownload.targetFile.diskSpaceAvailable; |
|
638 } catch(ex) { } |
|
639 |
|
640 let contentLength = aDownload.size; |
|
641 if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) { |
|
642 MetroDownloadsView.showAlert(aDownload.target.spec.replace("file:", "download:"), |
|
643 strings.GetStringFromName("alertDownloadsNoSpace"), |
|
644 strings.GetStringFromName("alertDownloadsSize")); |
|
645 MetroDownloadsView.cancelDownload(aDownload); |
|
646 } |
|
647 }, |
|
648 |
|
649 onDownloadStateChange: function(aState, aDownload) { }, |
|
650 onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { }, |
|
651 onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { }, |
|
652 |
|
653 ////////////////////////////////////////////////////////////////////////////// |
|
654 //// nsISupports |
|
655 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener]) |
|
656 }; |