!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 //@line 44 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/mozapps/downloads/content/downloads.js"
2
3 ////////////////////////////////////////////////////////////////////////////////
4 //// Globals
5
6 const PREF_BDM_CLOSEWHENDONE = "browser.download.manager.closeWhenDone";
7 const PREF_BDM_ALERTONEXEOPEN = "browser.download.manager.alertOnEXEOpen";
8
9 const nsLocalFile = Components.Constructor("@mozilla.org/file/local;1",
10 "nsILocalFile", "initWithPath");
11
12 var Cc = Components.classes;
13 var Ci = Components.interfaces;
14 let Cu = Components.utils;
15 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
16 Cu.import("resource://gre/modules/DownloadUtils.jsm");
17 Cu.import("resource://gre/modules/PluralForm.jsm");
18
19 const nsIDM = Ci.nsIDownloadManager;
20
21 let gDownloadManager = Cc["@mozilla.org/download-manager;1"].getService(nsIDM);
22 let gDownloadListener = null;
23 let gDownloadsView = null;
24 let gSearchBox = null;
25 let gSearchTerms = [];
26 let gBuilder = 0;
27
28 // This variable is used when performing commands on download items and gives
29 // the command the ability to do something after all items have been operated
30 // on. The following convention is used to handle the value of the variable:
31 // whenever we aren't performing a command, the value is |undefined|; just
32 // before executing commands, the value will be set to |null|; and when
33 // commands want to create a callback, they set the value to be a callback
34 // function to be executed after all download items have been visited.
35 let gPerformAllCallback;
36
37 // Control the performance of the incremental list building by setting how many
38 // milliseconds to wait before building more of the list and how many items to
39 // add between each delay.
40 const gListBuildDelay = 300;
41 const gListBuildChunk = 3;
42
43 // Array of download richlistitem attributes to check when searching
44 const gSearchAttributes = [
45 "target",
46 "status",
47 "dateTime",
48 ];
49
50 // If the user has interacted with the window in a significant way, we should
51 // not auto-close the window. Tough UI decisions about what is "significant."
52 var gUserInteracted = false;
53
54 // These strings will be converted to the corresponding ones from the string
55 // bundle on startup.
56 let gStr = {
57 paused: "paused",
58 cannotPause: "cannotPause",
59 doneStatus: "doneStatus",
60 doneSize: "doneSize",
61 doneSizeUnknown: "doneSizeUnknown",
62 stateFailed: "stateFailed",
63 stateCanceled: "stateCanceled",
64 stateBlockedParentalControls: "stateBlocked",
65 stateBlockedPolicy: "stateBlockedPolicy",
66 stateDirty: "stateDirty",
67 yesterday: "yesterday",
68 monthDate: "monthDate",
69 downloadsTitleFiles: "downloadsTitleFiles",
70 downloadsTitlePercent: "downloadsTitlePercent",
71 fileExecutableSecurityWarningTitle: "fileExecutableSecurityWarningTitle",
72 fileExecutableSecurityWarningDontAsk: "fileExecutableSecurityWarningDontAsk"
73 };
74
75 // The statement to query for downloads that are active or match the search
76 let gStmt = gDownloadManager.DBConnection.createStatement(
77 "SELECT id, target, name, source, state, startTime, endTime, referrer, " +
78 "currBytes, maxBytes, state IN (?1, ?2, ?3, ?4, ?5) isActive " +
79 "FROM moz_downloads " +
80 "ORDER BY isActive DESC, endTime DESC, startTime DESC");
81
82 ////////////////////////////////////////////////////////////////////////////////
83 //// Utility Functions
84
getDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
85 function getDownload(aID)
86 {
87 return document.getElementById("dl" + aID);
88 }
89
90 ////////////////////////////////////////////////////////////////////////////////
91 //// Start/Stop Observers
92
downloadCompleted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
93 function downloadCompleted(aDownload)
94 {
95 // The download is changing state, so update the clear list button
96 updateClearListButton();
97
98 // Wrap this in try...catch since this can be called while shutting down...
99 // it doesn't really matter if it fails then since well.. we're shutting down
100 // and there's no UI to update!
101 try {
102 let dl = getDownload(aDownload.id);
103
104 // Update attributes now that we've finished
105 dl.setAttribute("startTime", Math.round(aDownload.startTime / 1000));
106 dl.setAttribute("endTime", Date.now());
107 dl.setAttribute("currBytes", aDownload.amountTransferred);
108 dl.setAttribute("maxBytes", aDownload.size);
109
110 // Move the download below active if it should stay in the list
111 if (downloadMatchesSearch(dl)) {
112 // Iterate down until we find a non-active download
113 let next = dl.nextSibling;
114 while (next && next.inProgress)
115 next = next.nextSibling;
116
117 // Move the item and color everything after where it moved from
118 let fixup = dl.nextSibling;
119 gDownloadsView.insertBefore(dl, next);
120 stripeifyList(fixup);
121 } else {
122 removeFromView(dl);
123 }
124
125 // getTypeFromFile fails if it can't find a type for this file.
126 try {
127 // Refresh the icon, so that executable icons are shown.
128 var mimeService = Cc["@mozilla.org/mime;1"].
129 getService(Ci.nsIMIMEService);
130 var contentType = mimeService.getTypeFromFile(aDownload.targetFile);
131
132 var listItem = getDownload(aDownload.id)
133 var oldImage = listItem.getAttribute("image");
134 // Tacking on contentType bypasses cache
135 listItem.setAttribute("image", oldImage + "&contentType=" + contentType);
136 } catch (e) { }
137
138 if (gDownloadManager.activeDownloadCount == 0)
139 document.title = document.documentElement.getAttribute("statictitle");
140 }
141 catch (e) { }
142 }
143
autoRemoveAndClose
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
144 function autoRemoveAndClose(aDownload)
145 {
146 var pref = Cc["@mozilla.org/preferences-service;1"].
147 getService(Ci.nsIPrefBranch);
148
149 if (gDownloadManager.activeDownloadCount == 0) {
150 // For the moment, just use the simple heuristic that if this window was
151 // opened by the download process, rather than by the user, it should
152 // auto-close if the pref is set that way. If the user opened it themselves,
153 // it should not close until they explicitly close it. Additionally, the
154 // preference to control the feature may not be set, so defaulting to
155 // keeping the window open.
156 let autoClose = false;
157 try {
158 autoClose = pref.getBoolPref(PREF_BDM_CLOSEWHENDONE);
159 } catch (e) { }
160 var autoOpened =
161 !window.opener || window.opener.location.href == window.location.href;
162 if (autoClose && autoOpened && !gUserInteracted) {
163 gCloseDownloadManager();
164 return true;
165 }
166 }
167
168 return false;
169 }
170
171 // This function can be overwritten by extensions that wish to place the
172 // Download Window in another part of the UI.
gCloseDownloadManager
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
173 function gCloseDownloadManager()
174 {
175 window.close();
176 }
177
178 ////////////////////////////////////////////////////////////////////////////////
179 //// Download Event Handlers
180
cancelDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
181 function cancelDownload(aDownload)
182 {
183 gDownloadManager.cancelDownload(aDownload.getAttribute("dlid"));
184
185 // XXXben -
186 // If we got here because we resumed the download, we weren't using a temp file
187 // because we used saveURL instead. (this is because the proper download mechanism
188 // employed by the helper app service isn't fully accessible yet... should be fixed...
189 // talk to bz...)
190 // the upshot is we have to delete the file if it exists.
191 var f = getLocalFileFromNativePathOrUrl(aDownload.getAttribute("file"));
192
193 if (f.exists())
194 f.remove(false);
195 }
196
pauseDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
197 function pauseDownload(aDownload)
198 {
199 var id = aDownload.getAttribute("dlid");
200 gDownloadManager.pauseDownload(id);
201 }
202
resumeDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
203 function resumeDownload(aDownload)
204 {
205 gDownloadManager.resumeDownload(aDownload.getAttribute("dlid"));
206 }
207
removeDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
208 function removeDownload(aDownload)
209 {
210 gDownloadManager.removeDownload(aDownload.getAttribute("dlid"));
211 }
212
retryDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
213 function retryDownload(aDownload)
214 {
215 removeFromView(aDownload);
216 gDownloadManager.retryDownload(aDownload.getAttribute("dlid"));
217 }
218
showDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
219 function showDownload(aDownload)
220 {
221 var f = getLocalFileFromNativePathOrUrl(aDownload.getAttribute("file"));
222
223 try {
224 // Show the directory containing the file and select the file
225 f.reveal();
226 } catch (e) {
227 // If reveal fails for some reason (e.g., it's not implemented on unix or
228 // the file doesn't exist), try using the parent if we have it.
229 let parent = f.parent.QueryInterface(Ci.nsILocalFile);
230 if (!parent)
231 return;
232
233 try {
234 // "Double click" the parent directory to show where the file should be
235 parent.launch();
236 } catch (e) {
237 // If launch also fails (probably because it's not implemented), let the
238 // OS handler try to open the parent
239 openExternal(parent);
240 }
241 }
242 }
243
onDownloadDblClick
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
244 function onDownloadDblClick(aEvent)
245 {
246 // Only do the default action for double primary clicks
247 if (aEvent.button == 0)
248 doDefaultForSelected();
249 }
250
openDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
251 function openDownload(aDownload)
252 {
253 var f = getLocalFileFromNativePathOrUrl(aDownload.getAttribute("file"));
254 if (f.isExecutable()) {
255 var dontAsk = false;
256 var pref = Cc["@mozilla.org/preferences-service;1"].
257 getService(Ci.nsIPrefBranch);
258 try {
259 dontAsk = !pref.getBoolPref(PREF_BDM_ALERTONEXEOPEN);
260 } catch (e) { }
261
262 if (!dontAsk) {
263 var strings = document.getElementById("downloadStrings");
264 var name = aDownload.getAttribute("target");
265 var message = strings.getFormattedString("fileExecutableSecurityWarning", [name, name]);
266
267 let title = gStr.fileExecutableSecurityWarningTitle;
268 let dontAsk = gStr.fileExecutableSecurityWarningDontAsk;
269
270 var promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"].
271 getService(Ci.nsIPromptService);
272 var checkbox = { value: false };
273 var open = promptSvc.confirmCheck(window, title, message, dontAsk, checkbox);
274
275 if (!open)
276 return;
277 pref.setBoolPref(PREF_BDM_ALERTONEXEOPEN, !checkbox.value);
278 }
279 }
280 try {
281 f.launch();
282 } catch (ex) {
283 // if launch fails, try sending it through the system's external
284 // file: URL handler
285 openExternal(f);
286 }
287 }
288
openReferrer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
289 function openReferrer(aDownload)
290 {
291 openURL(getReferrerOrSource(aDownload));
292 }
293
copySourceLocation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
294 function copySourceLocation(aDownload)
295 {
296 var uri = aDownload.getAttribute("uri");
297 var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
298 getService(Ci.nsIClipboardHelper);
299
300 // Check if we should initialize a callback
301 if (gPerformAllCallback === null) {
302 let uris = [];
anon:303:26
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
303 gPerformAllCallback = function(aURI) aURI ? uris.push(aURI) :
304 clipboard.copyString(uris.join("\n"));
305 }
306
307 // We have a callback to use, so use it to add a uri
308 if (typeof gPerformAllCallback == "function")
309 gPerformAllCallback(uri);
310 else {
311 // It's a plain copy source, so copy it
312 clipboard.copyString(uri);
313 }
314 }
315
316 /**
317 * Remove the currently shown downloads from the download list.
318 */
clearDownloadList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
319 function clearDownloadList() {
320 // Clear the whole list if there's no search
321 if (gSearchTerms == "") {
322 gDownloadManager.cleanUp();
323 return;
324 }
325
326 // Remove each download starting from the end until we hit a download
327 // that is in progress
328 let item;
329 while ((item = gDownloadsView.lastChild) && !item.inProgress)
330 removeDownload(item);
331
332 // Clear the input as if the user did it and move focus to the list
333 gSearchBox.value = "";
334 gSearchBox.doCommand();
335 gDownloadsView.focus();
336 }
337
338 // This is called by the progress listener.
339 var gLastComputedMean = -1;
340 var gLastActiveDownloads = 0;
onUpdateProgress
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
341 function onUpdateProgress()
342 {
343 let numActiveDownloads = gDownloadManager.activeDownloadCount;
344
345 // Use the default title and reset "last" values if there's no downloads
346 if (numActiveDownloads == 0) {
347 document.title = document.documentElement.getAttribute("statictitle");
348 gLastComputedMean = -1;
349 gLastActiveDownloads = 0;
350
351 return;
352 }
353
354 // Establish the mean transfer speed and amount downloaded.
355 var mean = 0;
356 var base = 0;
357 var dls = gDownloadManager.activeDownloads;
358 while (dls.hasMoreElements()) {
359 let dl = dls.getNext().QueryInterface(Ci.nsIDownload);
360 if (dl.percentComplete < 100 && dl.size > 0) {
361 mean += dl.amountTransferred;
362 base += dl.size;
363 }
364 }
365
366 // Calculate the percent transferred, unless we don't have a total file size
367 let title = gStr.downloadsTitlePercent;
368 if (base == 0)
369 title = gStr.downloadsTitleFiles;
370 else
371 mean = Math.floor((mean / base) * 100);
372
373 // Update title of window
374 if (mean != gLastComputedMean || gLastActiveDownloads != numActiveDownloads) {
375 gLastComputedMean = mean;
376 gLastActiveDownloads = numActiveDownloads;
377
378 // Get the correct plural form and insert number of downloads and percent
379 title = PluralForm.get(numActiveDownloads, title);
380 title = replaceInsert(title, 1, numActiveDownloads);
381 title = replaceInsert(title, 2, mean);
382
383 document.title = title;
384 }
385 }
386
387 ////////////////////////////////////////////////////////////////////////////////
388 //// Startup, Shutdown
389
Startup
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
390 function Startup()
391 {
392 gDownloadsView = document.getElementById("downloadView");
393 gSearchBox = document.getElementById("searchbox");
394
395 // convert strings to those in the string bundle
396 let (sb = document.getElementById("downloadStrings")) {
anon:397:17
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
397 let getStr = function(string) sb.getString(string);
398 for (let [name, value] in Iterator(gStr))
399 gStr[name] = typeof value == "string" ? getStr(value) : value.map(getStr);
400 }
401
402 buildDownloadList(true);
403
404 // The DownloadProgressListener (DownloadProgressListener.js) handles progress
405 // notifications.
406 gDownloadListener = new DownloadProgressListener();
407 gDownloadManager.addListener(gDownloadListener);
408
409 // If the UI was displayed because the user interacted, we need to make sure
410 // we update gUserInteracted accordingly.
411 if (window.arguments[1] == Ci.nsIDownloadManagerUI.REASON_USER_INTERACTED)
412 gUserInteracted = true;
413
414 // downloads can finish before Startup() does, so check if the window should
415 // close and act accordingly
416 if (!autoRemoveAndClose())
417 gDownloadsView.focus();
418
419 let obs = Cc["@mozilla.org/observer-service;1"].
420 getService(Ci.nsIObserverService);
421 obs.addObserver(gDownloadObserver, "download-manager-remove-download", false);
422
423 // Clear the search box and move focus to the list on escape from the box
anon:424:42
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
424 gSearchBox.addEventListener("keypress", function(e) {
425 if (e.keyCode == e.DOM_VK_ESCAPE) {
426 // Clear the input as if the user did it
427 gSearchBox.value = "";
428 gSearchBox.doCommand();
429
430 // Move focus to the list instead of closing the window
431 gDownloadsView.focus();
432 e.preventDefault();
433 }
434 }, true);
435 }
436
Shutdown
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
437 function Shutdown()
438 {
439 gDownloadManager.removeListener(gDownloadListener);
440
441 let obs = Cc["@mozilla.org/observer-service;1"].
442 getService(Ci.nsIObserverService);
443 obs.removeObserver(gDownloadObserver, "download-manager-remove-download");
444
445 clearTimeout(gBuilder);
446 gStmt.reset();
447 gStmt.finalize();
448 }
449
450 let gDownloadObserver = {
gdo_observe
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
451 observe: function gdo_observe(aSubject, aTopic, aData) {
452 switch (aTopic) {
453 case "download-manager-remove-download":
454 // A null subject here indicates "remove all"
455 if (!aSubject) {
456 // Rebuild the default view
457 buildDownloadList(true);
458 break;
459 }
460
461 // Otherwise, remove a single download
462 let id = aSubject.QueryInterface(Ci.nsISupportsPRUint32);
463 let dl = getDownload(id.data);
464 removeFromView(dl);
465 break;
466 }
467 }
468 };
469
470 ////////////////////////////////////////////////////////////////////////////////
471 //// View Context Menus
472
473 var gContextMenus = [
474 // DOWNLOAD_DOWNLOADING
475 [
476 "menuitem_pause"
477 , "menuitem_cancel"
478 , "menuseparator"
479 , "menuitem_show"
480 , "menuseparator"
481 , "menuitem_openReferrer"
482 , "menuitem_copyLocation"
483 , "menuseparator"
484 , "menuitem_selectAll"
485 ],
486 // DOWNLOAD_FINISHED
487 [
488 "menuitem_open"
489 , "menuitem_show"
490 , "menuseparator"
491 , "menuitem_openReferrer"
492 , "menuitem_copyLocation"
493 , "menuseparator"
494 , "menuitem_selectAll"
495 , "menuseparator"
496 , "menuitem_removeFromList"
497 ],
498 // DOWNLOAD_FAILED
499 [
500 "menuitem_retry"
501 , "menuseparator"
502 , "menuitem_openReferrer"
503 , "menuitem_copyLocation"
504 , "menuseparator"
505 , "menuitem_selectAll"
506 , "menuseparator"
507 , "menuitem_removeFromList"
508 ],
509 // DOWNLOAD_CANCELED
510 [
511 "menuitem_retry"
512 , "menuseparator"
513 , "menuitem_openReferrer"
514 , "menuitem_copyLocation"
515 , "menuseparator"
516 , "menuitem_selectAll"
517 , "menuseparator"
518 , "menuitem_removeFromList"
519 ],
520 // DOWNLOAD_PAUSED
521 [
522 "menuitem_resume"
523 , "menuitem_cancel"
524 , "menuseparator"
525 , "menuitem_show"
526 , "menuseparator"
527 , "menuitem_openReferrer"
528 , "menuitem_copyLocation"
529 , "menuseparator"
530 , "menuitem_selectAll"
531 ],
532 // DOWNLOAD_QUEUED
533 [
534 "menuitem_cancel"
535 , "menuseparator"
536 , "menuitem_show"
537 , "menuseparator"
538 , "menuitem_openReferrer"
539 , "menuitem_copyLocation"
540 , "menuseparator"
541 , "menuitem_selectAll"
542 ],
543 // DOWNLOAD_BLOCKED_PARENTAL
544 [
545 "menuitem_openReferrer"
546 , "menuitem_copyLocation"
547 , "menuseparator"
548 , "menuitem_selectAll"
549 , "menuseparator"
550 , "menuitem_removeFromList"
551 ],
552 // DOWNLOAD_SCANNING
553 [
554 "menuitem_show"
555 , "menuseparator"
556 , "menuitem_openReferrer"
557 , "menuitem_copyLocation"
558 , "menuseparator"
559 , "menuitem_selectAll"
560 ],
561 // DOWNLOAD_DIRTY
562 [
563 "menuitem_openReferrer"
564 , "menuitem_copyLocation"
565 , "menuseparator"
566 , "menuitem_selectAll"
567 , "menuseparator"
568 , "menuitem_removeFromList"
569 ],
570 // DOWNLOAD_BLOCKED_POLICY
571 [
572 "menuitem_openReferrer"
573 , "menuitem_copyLocation"
574 , "menuseparator"
575 , "menuitem_selectAll"
576 , "menuseparator"
577 , "menuitem_removeFromList"
578 ]
579 ];
580
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
581 function buildContextMenu(aEvent)
582 {
583 if (aEvent.target.id != "downloadContextMenu")
584 return false;
585
586 var popup = document.getElementById("downloadContextMenu");
587 while (popup.hasChildNodes())
588 popup.removeChild(popup.firstChild);
589
590 if (gDownloadsView.selectedItem) {
591 let dl = gDownloadsView.selectedItem;
592 let idx = parseInt(dl.getAttribute("state"));
593 if (idx < 0)
594 idx = 0;
595
596 var menus = gContextMenus[idx];
597 for (let i = 0; i < menus.length; ++i) {
598 let menuitem = document.getElementById(menus[i]).cloneNode(true);
599 let cmd = menuitem.getAttribute("cmd");
600 if (cmd)
601 menuitem.disabled = !gDownloadViewController.isCommandEnabled(cmd, dl);
602
603 popup.appendChild(menuitem);
604 }
605
606 return true;
607 }
608
609 return false;
610 }
611
612 ////////////////////////////////////////////////////////////////////////////////
613 //// Drag and Drop
614
615 var gDownloadDNDObserver =
616 {
onDragOver
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
617 onDragOver: function (aEvent, aFlavour, aDragSession)
618 {
619 aDragSession.canDrop = true;
620 },
621
onDrop
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
622 onDrop: function(aEvent, aXferData, aDragSession)
623 {
624 var split = aXferData.data.split("\n");
625 var url = split[0];
626 if (url != aXferData.data) { //do nothing, not a valid URL
627 var name = split[1];
628 saveURL(url, name, null, true, true);
629 }
630 },
631 _flavourSet: null,
getSupportedFlavours
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
632 getSupportedFlavours: function ()
633 {
634 if (!this._flavourSet) {
635 this._flavourSet = new FlavourSet();
636 this._flavourSet.appendFlavour("text/x-moz-url");
637 this._flavourSet.appendFlavour("text/unicode");
638 }
639 return this._flavourSet;
640 }
641 }
642
643 ////////////////////////////////////////////////////////////////////////////////
644 //// Command Updating and Command Handlers
645
646 var gDownloadViewController = {
isCommandEnabled
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
647 isCommandEnabled: function(aCommand, aItem)
648 {
649 let dl = aItem;
650 let download = null; // used for getting an nsIDownload object
651
652 switch (aCommand) {
653 case "cmd_cancel":
654 return dl.inProgress;
655 case "cmd_open": {
656 let file = getLocalFileFromNativePathOrUrl(dl.getAttribute("file"));
657 return dl.openable && file.exists();
658 }
659 case "cmd_show": {
660 let file = getLocalFileFromNativePathOrUrl(dl.getAttribute("file"));
661 return file.exists();
662 }
663 case "cmd_pause":
664 download = gDownloadManager.getDownload(dl.getAttribute("dlid"));
665 return dl.inProgress && !dl.paused && download.resumable;
666 case "cmd_pauseResume":
667 download = gDownloadManager.getDownload(dl.getAttribute("dlid"));
668 return (dl.inProgress || dl.paused) && download.resumable;
669 case "cmd_resume":
670 download = gDownloadManager.getDownload(dl.getAttribute("dlid"));
671 return dl.paused && download.resumable;
672 case "cmd_openReferrer":
673 return dl.hasAttribute("referrer");
674 case "cmd_removeFromList":
675 case "cmd_retry":
676 return dl.removable;
677 case "cmd_copyLocation":
678 return true;
679 }
680 return false;
681 },
682
doCommand
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
683 doCommand: function(aCommand, aItem)
684 {
685 if (this.isCommandEnabled(aCommand, aItem))
686 this.commands[aCommand](aItem);
687 },
688
689 commands: {
cmd_cancel
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
690 cmd_cancel: function(aSelectedItem) {
691 cancelDownload(aSelectedItem);
692 },
cmd_open
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
693 cmd_open: function(aSelectedItem) {
694 openDownload(aSelectedItem);
695 },
cmd_openReferrer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
696 cmd_openReferrer: function(aSelectedItem) {
697 openReferrer(aSelectedItem);
698 },
cmd_pause
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
699 cmd_pause: function(aSelectedItem) {
700 pauseDownload(aSelectedItem);
701 },
cmd_pauseResume
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
702 cmd_pauseResume: function(aSelectedItem) {
703 if (aSelectedItem.paused)
704 this.cmd_resume(aSelectedItem);
705 else
706 this.cmd_pause(aSelectedItem);
707 },
cmd_removeFromList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
708 cmd_removeFromList: function(aSelectedItem) {
709 removeDownload(aSelectedItem);
710 },
cmd_resume
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
711 cmd_resume: function(aSelectedItem) {
712 resumeDownload(aSelectedItem);
713 },
cmd_retry
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
714 cmd_retry: function(aSelectedItem) {
715 retryDownload(aSelectedItem);
716 },
cmd_show
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
717 cmd_show: function(aSelectedItem) {
718 showDownload(aSelectedItem);
719 },
cmd_copyLocation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
720 cmd_copyLocation: function(aSelectedItem) {
721 copySourceLocation(aSelectedItem);
722 },
723 }
724 };
725
726 /**
727 * Helper function to do commands.
728 *
729 * @param aCmd
730 * The command to be performed.
731 * @param aItem
732 * The richlistitem that represents the download that will have the
733 * command performed on it. If this is null, the command is performed on
734 * all downloads. If the item passed in is not a richlistitem that
735 * represents a download, it will walk up the parent nodes until it finds
736 * a DOM node that is.
737 */
performCommand
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
738 function performCommand(aCmd, aItem)
739 {
740 let elm = aItem;
741 if (!elm) {
742 // If we don't have a desired download item, do the command for all
743 // selected items. Initialize the callback to null so commands know to add
744 // a callback if they want. We will call the callback with empty arguments
745 // after performing the command on each selected download item.
746 gPerformAllCallback = null;
747
748 // Convert the nodelist into an array to keep a copy of the download items
749 let items = [];
750 for (let i = gDownloadsView.selectedItems.length; --i >= 0; )
751 items.unshift(gDownloadsView.selectedItems[i]);
752
753 // Do the command for each download item
754 for each (let item in items)
755 performCommand(aCmd, item);
756
757 // Call the callback with no arguments and reset because we're done
758 if (typeof gPerformAllCallback == "function")
759 gPerformAllCallback();
760 gPerformAllCallback = undefined;
761
762 return;
763 } else {
764 while (elm.nodeName != "richlistitem" ||
765 elm.getAttribute("type") != "download")
766 elm = elm.parentNode;
767 }
768
769 gDownloadViewController.doCommand(aCmd, elm);
770 }
771
setSearchboxFocus
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
772 function setSearchboxFocus()
773 {
774 gSearchBox.focus();
775 gSearchBox.select();
776 }
777
openExternal
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
778 function openExternal(aFile)
779 {
780 var uri = Cc["@mozilla.org/network/io-service;1"].
781 getService(Ci.nsIIOService).newFileURI(aFile);
782
783 var protocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
784 getService(Ci.nsIExternalProtocolService);
785 protocolSvc.loadUrl(uri);
786
787 return;
788 }
789
790 ////////////////////////////////////////////////////////////////////////////////
791 //// Utility Functions
792
793 /**
794 * Create a download richlistitem with the provided attributes. Some attributes
795 * are *required* while optional ones will only be set on the item if provided.
796 *
797 * @param aAttrs
798 * An object that must have the following properties: dlid, file,
799 * target, uri, state, progress, startTime, endTime, currBytes,
800 * maxBytes; optional properties: referrer
801 * @return An initialized download richlistitem
802 */
createDownloadItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
803 function createDownloadItem(aAttrs)
804 {
805 let dl = document.createElement("richlistitem");
806
807 // Copy the attributes from the argument into the item
808 for (let attr in aAttrs)
809 dl.setAttribute(attr, aAttrs[attr]);
810
811 // Initialize other attributes
812 dl.setAttribute("type", "download");
813 dl.setAttribute("id", "dl" + aAttrs.dlid);
814 dl.setAttribute("image", "moz-icon://" + aAttrs.file + "?size=32");
815 dl.setAttribute("lastSeconds", Infinity);
816
817 // Initialize more complex attributes
818 updateTime(dl);
819 updateStatus(dl);
820
821 try {
822 let file = getLocalFileFromNativePathOrUrl(aAttrs.file);
823 dl.setAttribute("path", file.nativePath || file.path);
824 return dl;
825 } catch (e) {
826 // aFile might not be a file: url or a valid native path
827 // see bug #392386 for details
828 }
829 return null;
830 }
831
832 /**
833 * Updates the disabled state of the buttons of a downlaod.
834 *
835 * @param aItem
836 * The richlistitem representing the download.
837 */
updateButtons
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
838 function updateButtons(aItem)
839 {
840 let buttons = aItem.buttons;
841
842 for (let i = 0; i < buttons.length; ++i) {
843 let cmd = buttons[i].getAttribute("cmd");
844 let enabled = gDownloadViewController.isCommandEnabled(cmd, aItem);
845 buttons[i].disabled = !enabled;
846
847 if ("cmd_pause" == cmd && !enabled) {
848 // We need to add the tooltip indicating that the download cannot be
849 // paused now.
850 buttons[i].setAttribute("tooltiptext", gStr.cannotPause);
851 }
852 }
853 }
854
855 /**
856 * Updates the status for a download item depending on its state
857 *
858 * @param aItem
859 * The richlistitem that has various download attributes.
860 * @param aDownload
861 * The nsDownload from the backend. This is an optional parameter, but
862 * is useful for certain states such as DOWNLOADING.
863 */
updateStatus
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
864 function updateStatus(aItem, aDownload) {
865 let status = "";
866 let statusTip = "";
867
868 let state = Number(aItem.getAttribute("state"));
869 switch (state) {
870 case nsIDM.DOWNLOAD_PAUSED:
871 {
872 let currBytes = Number(aItem.getAttribute("currBytes"));
873 let maxBytes = Number(aItem.getAttribute("maxBytes"));
874
875 let transfer = DownloadUtils.getTransferTotal(currBytes, maxBytes);
876 status = replaceInsert(gStr.paused, 1, transfer);
877
878 break;
879 }
880 case nsIDM.DOWNLOAD_DOWNLOADING:
881 {
882 let currBytes = Number(aItem.getAttribute("currBytes"));
883 let maxBytes = Number(aItem.getAttribute("maxBytes"));
884 // If we don't have an active download, assume 0 bytes/sec
885 let speed = aDownload ? aDownload.speed : 0;
886 let lastSec = Number(aItem.getAttribute("lastSeconds"));
887
888 let newLast;
889 [status, newLast] =
890 DownloadUtils.getDownloadStatus(currBytes, maxBytes, speed, lastSec);
891
892 // Update lastSeconds to be the new value
893 aItem.setAttribute("lastSeconds", newLast);
894
895 break;
896 }
897 case nsIDM.DOWNLOAD_FINISHED:
898 case nsIDM.DOWNLOAD_FAILED:
899 case nsIDM.DOWNLOAD_CANCELED:
900 case nsIDM.DOWNLOAD_BLOCKED_PARENTAL:
901 case nsIDM.DOWNLOAD_BLOCKED_POLICY:
902 case nsIDM.DOWNLOAD_DIRTY:
903 {
904 let (stateSize = {}) {
anon:905:45
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
905 stateSize[nsIDM.DOWNLOAD_FINISHED] = function() {
906 // Display the file size, but show "Unknown" for negative sizes
907 let fileSize = Number(aItem.getAttribute("maxBytes"));
908 let sizeText = gStr.doneSizeUnknown;
909 if (fileSize >= 0) {
910 let [size, unit] = DownloadUtils.convertByteUnits(fileSize);
911 sizeText = replaceInsert(gStr.doneSize, 1, size);
912 sizeText = replaceInsert(sizeText, 2, unit);
913 }
914 return sizeText;
915 };
anon:916:43
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
916 stateSize[nsIDM.DOWNLOAD_FAILED] = function() gStr.stateFailed;
anon:917:45
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
917 stateSize[nsIDM.DOWNLOAD_CANCELED] = function() gStr.stateCanceled;
anon:918:53
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
918 stateSize[nsIDM.DOWNLOAD_BLOCKED_PARENTAL] = function() gStr.stateBlockedParentalControls;
anon:919:51
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
919 stateSize[nsIDM.DOWNLOAD_BLOCKED_POLICY] = function() gStr.stateBlockedPolicy;
anon:920:42
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
920 stateSize[nsIDM.DOWNLOAD_DIRTY] = function() gStr.stateDirty;
921
922 // Insert 1 is the download size or download state
923 status = replaceInsert(gStr.doneStatus, 1, stateSize[state]());
924 }
925
926 let [displayHost, fullHost] =
927 DownloadUtils.getURIHost(getReferrerOrSource(aItem));
928 // Insert 2 is the eTLD + 1 or other variations of the host
929 status = replaceInsert(status, 2, displayHost);
930 // Set the tooltip to be the full host
931 statusTip = fullHost;
932
933 break;
934 }
935 }
936
937 aItem.setAttribute("status", status);
938 aItem.setAttribute("statusTip", statusTip != "" ? statusTip : status);
939 }
940
941 /**
942 * Updates the time that gets shown for completed download items
943 *
944 * @param aItem
945 * The richlistitem representing a download in the UI
946 */
updateTime
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
947 function updateTime(aItem)
948 {
949 // Don't bother updating for things that aren't finished
950 if (aItem.inProgress)
951 return;
952
953 let dts = Cc["@mozilla.org/intl/scriptabledateformat;1"].
954 getService(Ci.nsIScriptableDateFormat);
955
956 // Figure out when today begins
957 let now = new Date();
958 let today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
959
960 // Get the end time to display
961 let end = new Date(parseInt(aItem.getAttribute("endTime")));
962
963 // Figure out if the end time is from today, yesterday, this week, etc.
964 let dateTime;
965 if (end >= today) {
966 // Download finished after today started, show the time
967 dateTime = dts.FormatTime("", dts.timeFormatNoSeconds,
968 end.getHours(), end.getMinutes(), 0);
969 } else if (today - end < (24 * 60 * 60 * 1000)) {
970 // Download finished after yesterday started, show yesterday
971 dateTime = gStr.yesterday;
972 } else if (today - end < (6 * 24 * 60 * 60 * 1000)) {
973 // Download finished after last week started, show day of week
974 dateTime = end.toLocaleFormat("%A");
975 } else {
976 // Download must have been from some time ago.. show month/day
977 let month = end.toLocaleFormat("%B");
978 // Remove leading 0 by converting the date string to a number
979 let date = Number(end.toLocaleFormat("%d"));
980 dateTime = replaceInsert(gStr.monthDate, 1, month);
981 dateTime = replaceInsert(dateTime, 2, date);
982 }
983
984 aItem.setAttribute("dateTime", dateTime);
985
986 // Set the tooltip to be the full date and time
987 let dateTimeTip = dts.FormatDateTime("",
988 dts.dateFormatLong,
989 dts.timeFormatNoSeconds,
990 end.getFullYear(),
991 end.getMonth() + 1,
992 end.getDate(),
993 end.getHours(),
994 end.getMinutes(),
995 0);
996
997 aItem.setAttribute("dateTimeTip", dateTimeTip);
998 }
999
1000 /**
1001 * Helper function to replace a placeholder string with a real string
1002 *
1003 * @param aText
1004 * Source text containing placeholder (e.g., #1)
1005 * @param aIndex
1006 * Index number of placeholder to replace
1007 * @param aValue
1008 * New string to put in place of placeholder
1009 * @return The string with placeholder replaced with the new string
1010 */
replaceInsert
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1011 function replaceInsert(aText, aIndex, aValue)
1012 {
1013 return aText.replace("#" + aIndex, aValue);
1014 }
1015
1016 /**
1017 * Perform the default action for the currently selected download item
1018 */
doDefaultForSelected
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1019 function doDefaultForSelected()
1020 {
1021 // Make sure we have something selected
1022 let item = gDownloadsView.selectedItem;
1023 if (!item)
1024 return;
1025
1026 // Get the default action (first item in the menu)
1027 let state = Number(item.getAttribute("state"));
1028 let menuitem = document.getElementById(gContextMenus[state][0]);
1029
1030 // Try to do the action if the command is enabled
1031 gDownloadViewController.doCommand(menuitem.getAttribute("cmd"), item);
1032 }
1033
removeFromView
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1034 function removeFromView(aDownload)
1035 {
1036 // Make sure we have an item to remove
1037 if (!aDownload) return;
1038
1039 let index = gDownloadsView.selectedIndex;
1040 gDownloadsView.removeChild(aDownload);
1041 gDownloadsView.selectedIndex = Math.min(index, gDownloadsView.itemCount - 1);
1042
1043 // Color everything after from the newly selected item
1044 stripeifyList(gDownloadsView.selectedItem);
1045
1046 // We might have removed the last item, so update the clear list button
1047 updateClearListButton();
1048 }
1049
getReferrerOrSource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1050 function getReferrerOrSource(aDownload)
1051 {
1052 // Give the referrer if we have it set
1053 if (aDownload.hasAttribute("referrer"))
1054 return aDownload.getAttribute("referrer");
1055
1056 // Otherwise, provide the source
1057 return aDownload.getAttribute("uri");
1058 }
1059
1060 /**
1061 * Initiate building the download list to have the active downloads followed by
1062 * completed ones filtered by the search term if necessary.
1063 *
1064 * @param aForceBuild
1065 * Force the list to be built even if the search terms don't change
1066 */
buildDownloadList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1067 function buildDownloadList(aForceBuild)
1068 {
1069 // Stringify the previous search
1070 let prevSearch = gSearchTerms.join(" ");
1071
1072 // Array of space-separated lower-case search terms
1073 gSearchTerms = gSearchBox.value.replace(/^\s+|\s+$/g, "").
1074 toLowerCase().split(/\s+/);
1075
1076 // Unless forced, don't rebuild the download list if the search didn't change
1077 if (!aForceBuild && gSearchTerms.join(" ") == prevSearch)
1078 return;
1079
1080 // Clear out values before using them
1081 clearTimeout(gBuilder);
1082 gStmt.reset();
1083
1084 // Clear the list before adding items by replacing with a shallow copy
1085 let (empty = gDownloadsView.cloneNode(false)) {
1086 gDownloadsView.parentNode.replaceChild(empty, gDownloadsView);
1087 gDownloadsView = empty;
1088 }
1089
1090 try {
1091 gStmt.bindInt32Parameter(0, nsIDM.DOWNLOAD_NOTSTARTED);
1092 gStmt.bindInt32Parameter(1, nsIDM.DOWNLOAD_DOWNLOADING);
1093 gStmt.bindInt32Parameter(2, nsIDM.DOWNLOAD_PAUSED);
1094 gStmt.bindInt32Parameter(3, nsIDM.DOWNLOAD_QUEUED);
1095 gStmt.bindInt32Parameter(4, nsIDM.DOWNLOAD_SCANNING);
1096 } catch (e) {
1097 // Something must have gone wrong when binding, so clear and quit
1098 gStmt.reset();
1099 return;
1100 }
1101
1102 // Take a quick break before we actually start building the list
anon:1103:24
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1103 gBuilder = setTimeout(function() {
1104 // Start building the list and select the first item
1105 stepListBuilder(1);
1106 gDownloadsView.selectedIndex = 0;
1107
1108 // We just tried to add a single item, so we probably need to enable
1109 updateClearListButton();
1110 }, 0);
1111 }
1112
1113 /**
1114 * Incrementally build the download list by adding at most the requested number
1115 * of items if there are items to add. After doing that, it will schedule
1116 * another chunk of items specified by gListBuildDelay and gListBuildChunk.
1117 *
1118 * @param aNumItems
1119 * Number of items to add to the list before taking a break
1120 */
stepListBuilder
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1121 function stepListBuilder(aNumItems) {
1122 try {
1123 // If we're done adding all items, we can quit
1124 if (!gStmt.executeStep()) {
1125 // Send a notification that we finished, but wait for clear list to update
1126 updateClearListButton();
anon:1127:17
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1127 setTimeout(function() Cc["@mozilla.org/observer-service;1"].
1128 getService(Ci.nsIObserverService).
1129 notifyObservers(window, "download-manager-ui-done", null), 0);
1130
1131 return;
1132 }
1133
1134 // Try to get the attribute values from the statement
1135 let attrs = {
1136 dlid: gStmt.getInt64(0),
1137 file: gStmt.getString(1),
1138 target: gStmt.getString(2),
1139 uri: gStmt.getString(3),
1140 state: gStmt.getInt32(4),
1141 startTime: Math.round(gStmt.getInt64(5) / 1000),
1142 endTime: Math.round(gStmt.getInt64(6) / 1000),
1143 currBytes: gStmt.getInt64(8),
1144 maxBytes: gStmt.getInt64(9)
1145 };
1146
1147 // Only add the referrer if it's not null
1148 let (referrer = gStmt.getString(7)) {
1149 if (referrer)
1150 attrs.referrer = referrer;
1151 }
1152
1153 // If the download is active, grab the real progress, otherwise default 100
1154 let isActive = gStmt.getInt32(10);
1155 attrs.progress = isActive ? gDownloadManager.getDownload(attrs.dlid).
1156 percentComplete : 100;
1157
1158 // Make the item and add it to the end if it's active or matches the search
1159 let item = createDownloadItem(attrs);
1160 if (item && (isActive || downloadMatchesSearch(item))) {
1161 // Add item to the end and color just that one item
1162 gDownloadsView.appendChild(item);
1163 stripeifyList(item);
1164
1165 // Because of the joys of XBL, we can't update the buttons until the
1166 // download object is in the document.
1167 updateButtons(item);
1168 } else {
1169 // We didn't add an item, so bump up the number of items to process, but
1170 // not a whole number so that we eventually do pause for a chunk break
1171 aNumItems += .9;
1172 }
1173 } catch (e) {
1174 // Something went wrong when stepping or getting values, so clear and quit
1175 gStmt.reset();
1176 return;
1177 }
1178
1179 // Add another item to the list if we should; otherwise, let the UI update
1180 // and continue later
1181 if (aNumItems > 1) {
1182 stepListBuilder(aNumItems - 1);
1183 } else {
1184 // Use a shorter delay for earlier downloads to display them faster
1185 let delay = Math.min(gDownloadsView.itemCount * 10, gListBuildDelay);
1186 gBuilder = setTimeout(stepListBuilder, delay, gListBuildChunk);
1187 }
1188 }
1189
1190 /**
1191 * Add a download to the front of the download list
1192 *
1193 * @param aDownload
1194 * The nsIDownload to make into a richlistitem
1195 */
prependList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1196 function prependList(aDownload)
1197 {
1198 let attrs = {
1199 dlid: aDownload.id,
1200 file: aDownload.target.spec,
1201 target: aDownload.displayName,
1202 uri: aDownload.source.spec,
1203 state: aDownload.state,
1204 progress: aDownload.percentComplete,
1205 startTime: Math.round(aDownload.startTime / 1000),
1206 endTime: Date.now(),
1207 currBytes: aDownload.amountTransferred,
1208 maxBytes: aDownload.size
1209 };
1210
1211 // Make the item and add it to the beginning
1212 let item = createDownloadItem(attrs);
1213 if (item) {
1214 // Add item to the beginning and color the whole list
1215 gDownloadsView.insertBefore(item, gDownloadsView.firstChild);
1216 stripeifyList(item);
1217
1218 // Because of the joys of XBL, we can't update the buttons until the
1219 // download object is in the document.
1220 updateButtons(item);
1221
1222 // We might have added an item to an empty list, so update button
1223 updateClearListButton();
1224 }
1225 }
1226
1227 /**
1228 * Check if the download matches the current search term based on the texts
1229 * shown to the user. All search terms are checked to see if each matches any
1230 * of the displayed texts.
1231 *
1232 * @param aItem
1233 * Download richlistitem to check if it matches the current search
1234 * @return Boolean true if it matches the search; false otherwise
1235 */
downloadMatchesSearch
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1236 function downloadMatchesSearch(aItem)
1237 {
1238 // Search through the download attributes that are shown to the user and
1239 // make it into one big string for easy combined searching
1240 let combinedSearch = "";
1241 for each (let attr in gSearchAttributes)
1242 combinedSearch += aItem.getAttribute(attr).toLowerCase() + " ";
1243
1244 // Make sure each of the terms are found
1245 for each (let term in gSearchTerms)
1246 if (combinedSearch.search(term) == -1)
1247 return false;
1248
1249 return true;
1250 }
1251
1252 /**
1253 * Stripeify the download list by setting or clearing the "alternate" attribute
1254 * on items starting from a particular item and continuing to the end.
1255 *
1256 * @param aItem
1257 * Download rishlist item to start stripeifying
1258 */
stripeifyList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1259 function stripeifyList(aItem)
1260 {
1261 let alt = "alternate";
1262 // Set the item to be opposite of the other
anon:1263:17
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1263 let flipFrom = function(aOther) aOther && aOther.hasAttribute(alt) ?
1264 aItem.removeAttribute(alt) : aItem.setAttribute(alt, "true");
1265
1266 // Keep coloring items as the opposite of its previous until no more
1267 while (aItem) {
1268 flipFrom(aItem.previousSibling);
1269 aItem = aItem.nextSibling;
1270 }
1271 }
1272
1273 // we should be using real URLs all the time, but until
1274 // bug 239948 is fully fixed, this will do...
1275 //
1276 // note, this will thrown an exception if the native path
1277 // is not valid (for example a native Windows path on a Mac)
1278 // see bug #392386 for details
getLocalFileFromNativePathOrUrl
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1279 function getLocalFileFromNativePathOrUrl(aPathOrUrl)
1280 {
1281 if (aPathOrUrl.substring(0,7) == "file://") {
1282 // if this is a URL, get the file from that
1283 let ioSvc = Cc["@mozilla.org/network/io-service;1"].
1284 getService(Ci.nsIIOService);
1285
1286 // XXX it's possible that using a null char-set here is bad
1287 const fileUrl = ioSvc.newURI(aPathOrUrl, null, null).
1288 QueryInterface(Ci.nsIFileURL);
1289 return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile);
1290 } else {
1291 // if it's a pathname, create the nsILocalFile directly
1292 var f = new nsLocalFile(aPathOrUrl);
1293
1294 return f;
1295 }
1296 }
1297
1298 /**
1299 * Update the disabled state of the clear list button based on whether or not
1300 * there are items in the list that can potentially be removed.
1301 */
updateClearListButton
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1302 function updateClearListButton()
1303 {
1304 let button = document.getElementById("clearListButton");
1305 // The button is enabled if we have items in the list and we can clean up
1306 button.disabled = !(gDownloadsView.itemCount && gDownloadManager.canCleanUp);
1307 }