Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
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/. */
6 "use strict";
8 let Cu = Components.utils;
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
11 function dump(a) {
12 Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a);
13 }
15 XPCOMUtils.defineLazyModuleGetter(this, "Notifications",
16 "resource://gre/modules/Notifications.jsm");
18 const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download";
19 const URI_PAUSE_ICON = "drawable://pause";
20 const URI_CANCEL_ICON = "drawable://close";
21 const URI_RESUME_ICON = "drawable://play";
24 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
26 var Downloads = {
27 _initialized: false,
28 _dlmgr: null,
29 _progressAlert: null,
30 _privateDownloads: [],
31 _showingPrompt: false,
32 _downloadsIdMap: {},
34 _getLocalFile: function dl__getLocalFile(aFileURI) {
35 // if this is a URL, get the file from that
36 // XXX it's possible that using a null char-set here is bad
37 const fileUrl = Services.io.newURI(aFileURI, null, null).QueryInterface(Ci.nsIFileURL);
38 return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile);
39 },
41 init: function dl_init() {
42 if (this._initialized)
43 return;
44 this._initialized = true;
46 // Monitor downloads and display alerts
47 this._dlmgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
48 this._progressAlert = new AlertDownloadProgressListener();
49 this._dlmgr.addPrivacyAwareListener(this._progressAlert);
50 Services.obs.addObserver(this, "last-pb-context-exited", true);
51 },
53 openDownload: function dl_openDownload(aDownload) {
54 let fileUri = aDownload.target.spec;
55 let guid = aDownload.guid;
56 let f = this._getLocalFile(fileUri);
57 try {
58 f.launch();
59 } catch (ex) {
60 // in case we are not able to open the file (i.e. there is no app able to handle it)
61 // we just open the browser tab showing it
62 BrowserApp.addTab("about:downloads?id=" + guid);
63 }
64 },
66 cancelDownload: function dl_cancelDownload(aDownload) {
67 aDownload.cancel();
68 let fileURI = aDownload.target.spec;
69 let f = this._getLocalFile(fileURI);
71 OS.File.remove(f.path);
72 },
74 showCancelConfirmPrompt: function dl_showCancelConfirmPrompt(aDownload) {
75 if (this._showingPrompt)
76 return;
77 this._showingPrompt = true;
78 // Open a prompt that offers a choice to cancel the download
79 let title = Strings.browser.GetStringFromName("downloadCancelPromptTitle");
80 let message = Strings.browser.GetStringFromName("downloadCancelPromptMessage");
81 let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_YES +
82 Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_NO;
83 let choice = Services.prompt.confirmEx(null, title, message, flags,
84 null, null, null, null, {});
85 if (choice == 0)
86 this.cancelDownload(aDownload);
87 this._showingPrompt = false;
88 },
90 handleClickEvent: function dl_handleClickEvent(aDownload) {
91 // Only open the downloaded file if the download is complete
92 if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED)
93 this.openDownload(aDownload);
94 else if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING ||
95 aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_PAUSED)
96 this.showCancelConfirmPrompt(aDownload);
97 },
99 clickCallback: function dl_clickCallback(aDownloadId) {
100 this._dlmgr.getDownloadByGUID(aDownloadId, (function(status, download) {
101 if (Components.isSuccessCode(status))
102 this.handleClickEvent(download);
103 }).bind(this));
104 },
106 pauseClickCallback: function dl_buttonPauseCallback(aDownloadId) {
107 this._dlmgr.getDownloadByGUID(aDownloadId, (function(status, download) {
108 if (Components.isSuccessCode(status))
109 download.pause();
110 }).bind(this));
111 },
113 resumeClickCallback: function dl_buttonPauseCallback(aDownloadId) {
114 this._dlmgr.getDownloadByGUID(aDownloadId, (function(status, download) {
115 if (Components.isSuccessCode(status))
116 download.resume();
117 }).bind(this));
118 },
120 cancelClickCallback: function dl_buttonPauseCallback(aDownloadId) {
121 this._dlmgr.getDownloadByGUID(aDownloadId, (function(status, download) {
122 if (Components.isSuccessCode(status))
123 this.cancelDownload(download);
124 }).bind(this));
125 },
127 notificationCanceledCallback: function dl_notifCancelCallback(aId, aDownloadId) {
128 let notificationId = this._downloadsIdMap[aDownloadId];
129 if (notificationId && notificationId == aId)
130 delete this._downloadsIdMap[aDownloadId];
131 },
133 createNotification: function dl_createNotif(aDownload, aOptions) {
134 let notificationId = Notifications.create(aOptions);
135 this._downloadsIdMap[aDownload.guid] = notificationId;
136 },
138 updateNotification: function dl_updateNotif(aDownload, aOptions) {
139 let notificationId = this._downloadsIdMap[aDownload.guid];
140 if (notificationId)
141 Notifications.update(notificationId, aOptions);
142 },
144 cancelNotification: function dl_cleanNotif(aDownload) {
145 Notifications.cancel(this._downloadsIdMap[aDownload.guid]);
146 delete this._downloadsIdMap[aDownload.guid];
147 },
149 // observer for last-pb-context-exited
150 observe: function dl_observe(aSubject, aTopic, aData) {
151 let download;
152 while ((download = this._privateDownloads.pop())) {
153 try {
154 let notificationId = aDownload.guid;
155 Notifications.clear(notificationId);
156 Downloads.removeNotification(download);
157 } catch (e) {
158 dump("Error removing private download: " + e);
159 }
160 }
161 },
163 QueryInterface: function (aIID) {
164 if (!aIID.equals(Ci.nsISupports) &&
165 !aIID.equals(Ci.nsIObserver) &&
166 !aIID.equals(Ci.nsISupportsWeakReference))
167 throw Components.results.NS_ERROR_NO_INTERFACE;
168 return this;
169 }
170 };
172 const PAUSE_BUTTON = {
173 buttonId: "pause",
174 title : Strings.browser.GetStringFromName("alertDownloadsPause"),
175 icon : URI_PAUSE_ICON,
176 onClicked: function (aId, aCookie) {
177 Downloads.pauseClickCallback(aCookie);
178 }
179 };
181 const CANCEL_BUTTON = {
182 buttonId: "cancel",
183 title : Strings.browser.GetStringFromName("alertDownloadsCancel"),
184 icon : URI_CANCEL_ICON,
185 onClicked: function (aId, aCookie) {
186 Downloads.cancelClickCallback(aCookie);
187 }
188 };
190 const RESUME_BUTTON = {
191 buttonId: "resume",
192 title : Strings.browser.GetStringFromName("alertDownloadsResume"),
193 icon: URI_RESUME_ICON,
194 onClicked: function (aId, aCookie) {
195 Downloads.resumeClickCallback(aCookie);
196 }
197 };
199 function DownloadNotifOptions (aDownload, aTitle, aMessage) {
200 this.icon = URI_GENERIC_ICON_DOWNLOAD;
201 this.onCancel = function (aId, aCookie) {
202 Downloads.notificationCanceledCallback(aId, aCookie);
203 }
204 this.onClick = function (aId, aCookie) {
205 Downloads.clickCallback(aCookie);
206 }
207 this.title = aTitle;
208 this.message = aMessage;
209 this.buttons = null;
210 this.cookie = aDownload.guid;
211 this.persistent = true;
212 }
214 function DownloadProgressNotifOptions (aDownload, aButtons) {
215 DownloadNotifOptions.apply(this, [aDownload, aDownload.displayName, aDownload.percentComplete + "%"]);
216 this.ongoing = true;
217 this.progress = aDownload.percentComplete;
218 this.buttons = aButtons;
219 }
221 // AlertDownloadProgressListener is used to display progress in the alert notifications.
222 function AlertDownloadProgressListener() { }
224 AlertDownloadProgressListener.prototype = {
225 //////////////////////////////////////////////////////////////////////////////
226 //// nsIDownloadProgressListener
227 onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) {
228 let strings = Strings.browser;
229 let availableSpace = -1;
230 try {
231 // diskSpaceAvailable is not implemented on all systems
232 let availableSpace = aDownload.targetFile.diskSpaceAvailable;
233 } catch(ex) { }
234 let contentLength = aDownload.size;
235 if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
236 Downloads.updateNotification(aDownload, new DownloadNotifOptions(aDownload,
237 strings.GetStringFromName("alertDownloadsNoSpace"),
238 strings.GetStringFromName("alertDownloadsSize")));
239 aDownload.cancel();
240 }
242 if (aDownload.percentComplete == -1) {
243 // Undetermined progress is not supported yet
244 return;
245 }
247 Downloads.updateNotification(aDownload, new DownloadProgressNotifOptions(aDownload, [PAUSE_BUTTON, CANCEL_BUTTON]));
248 },
250 onDownloadStateChange: function(aState, aDownload) {
251 let state = aDownload.state;
252 switch (state) {
253 case Ci.nsIDownloadManager.DOWNLOAD_QUEUED: {
254 NativeWindow.toast.show(Strings.browser.GetStringFromName("alertDownloadsToast"), "long");
255 Downloads.createNotification(aDownload, new DownloadNotifOptions(aDownload,
256 Strings.browser.GetStringFromName("alertDownloadsStart2"),
257 aDownload.displayName));
258 break;
259 }
260 case Ci.nsIDownloadManager.DOWNLOAD_PAUSED: {
261 Downloads.updateNotification(aDownload, new DownloadProgressNotifOptions(aDownload, [RESUME_BUTTON, CANCEL_BUTTON]));
262 break;
263 }
264 case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
265 case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
266 case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL:
267 case Ci.nsIDownloadManager.DOWNLOAD_DIRTY:
268 case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: {
269 Downloads.cancelNotification(aDownload);
270 if (aDownload.isPrivate) {
271 let index = Downloads._privateDownloads.indexOf(aDownload);
272 if (index != -1) {
273 Downloads._privateDownloads.splice(index, 1);
274 }
275 }
277 if (state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
278 Downloads.createNotification(aDownload, new DownloadNotifOptions(aDownload,
279 Strings.browser.GetStringFromName("alertDownloadsDone2"),
280 aDownload.displayName));
281 }
282 break;
283 }
284 }
285 },
287 onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { },
288 onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { },
290 //////////////////////////////////////////////////////////////////////////////
291 //// nsISupports
292 QueryInterface: function (aIID) {
293 if (!aIID.equals(Ci.nsIDownloadProgressListener) &&
294 !aIID.equals(Ci.nsISupports))
295 throw Components.results.NS_ERROR_NO_INTERFACE;
296 return this;
297 }
298 };