123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- const EXPORTED_SYMBOLS = ["S4EDownloadService"];
- const CC = Components.classes;
- const CI = Components.interfaces;
- const CU = Components.utils;
- CU.import("resource://gre/modules/Services.jsm");
- CU.import("resource://gre/modules/PluralForm.jsm");
- CU.import("resource://gre/modules/DownloadUtils.jsm");
- CU.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
- CU.import("resource://gre/modules/XPCOMUtils.jsm");
- function S4EDownloadService(window, gBrowser, service, getters)
- {
- this._window = window;
- this._gBrowser = gBrowser;
- this._service = service;
- this._getters = getters;
- this._handler = new JSTransferHandler(this._window, this);
- }
- S4EDownloadService.prototype =
- {
- _window: null,
- _gBrowser: null,
- _service: null,
- _getters: null,
- _handler: null,
- _listening: false,
- _binding: false,
- _customizing: false,
- _lastTime: Infinity,
- _dlActive: false,
- _dlPaused: false,
- _dlFinished: false,
- _dlCountStr: null,
- _dlTimeStr: null,
- _dlProgressAvg: 0,
- _dlProgressMax: 0,
- _dlProgressMin: 0,
- _dlProgressType: "active",
- _dlNotifyTimer: 0,
- _dlNotifyGlowTimer: 0,
- init: function()
- {
- if(!this._getters.downloadButton)
- {
- this.uninit();
- return;
- }
- if(this._listening)
- {
- return;
- }
- this._handler.start();
- this._listening = true;
- this._lastTime = Infinity;
- this.updateBinding();
- this.updateStatus();
- },
- uninit: function()
- {
- if(!this._listening)
- {
- return;
- }
- this._listening = false;
- this._handler.stop();
- this.releaseBinding();
- },
- destroy: function()
- {
- this.uninit();
- this._handler.destroy();
- ["_window", "_gBrowser", "_service", "_getters", "_handler"].forEach(function(prop)
- {
- delete this[prop];
- }, this);
- },
- updateBinding: function()
- {
- if(!this._listening)
- {
- this.releaseBinding();
- return;
- }
- switch(this._service.downloadButtonAction)
- {
- case 1: // Default
- this.attachBinding();
- break;
- default:
- this.releaseBinding();
- break;
- }
- },
- attachBinding: function()
- {
- if(this._binding)
- {
- return;
- }
- let db = this._window.DownloadsButton;
- db._getAnchorS4EBackup = db.getAnchor;
- db.getAnchor = this.getAnchor.bind(this);
- db._releaseAnchorS4EBackup = db.releaseAnchor;
- db.releaseAnchor = function() {};
- this._binding = true;
- },
- releaseBinding: function()
- {
- if(!this._binding)
- {
- return;
- }
- let db = this._window.DownloadsButton;
- db.getAnchor = db._getAnchorS4EBackup;
- db.releaseAnchor = db._releaseAnchorS4EBackup;
- this._binding = false;
- },
- customizing: function(val)
- {
- this._customizing = val;
- },
- updateStatus: function(lastFinished)
- {
- if(!this._getters.downloadButton)
- {
- this.uninit();
- return;
- }
- let numActive = 0;
- let numPaused = 0;
- let activeTotalSize = 0;
- let activeTransferred = 0;
- let activeMaxProgress = -Infinity;
- let activeMinProgress = Infinity;
- let pausedTotalSize = 0;
- let pausedTransferred = 0;
- let pausedMaxProgress = -Infinity;
- let pausedMinProgress = Infinity;
- let maxTime = -Infinity;
- let dls = ((this.isPrivateWindow) ? this._handler.activePrivateEntries() : this._handler.activeEntries());
- for(let dl of dls)
- {
- if(dl.state == CI.nsIDownloadManager.DOWNLOAD_DOWNLOADING)
- {
- numActive++;
- if(dl.size > 0)
- {
- if(dl.speed > 0)
- {
- maxTime = Math.max(maxTime, (dl.size - dl.transferred) / dl.speed);
- }
- activeTotalSize += dl.size;
- activeTransferred += dl.transferred;
- let currentProgress = ((dl.transferred * 100) / dl.size);
- activeMaxProgress = Math.max(activeMaxProgress, currentProgress);
- activeMinProgress = Math.min(activeMinProgress, currentProgress);
- }
- }
- else if(dl.state == CI.nsIDownloadManager.DOWNLOAD_PAUSED)
- {
- numPaused++;
- if(dl.size > 0)
- {
- pausedTotalSize += dl.size;
- pausedTransferred += dl.transferred;
- let currentProgress = ((dl.transferred * 100) / dl.size);
- pausedMaxProgress = Math.max(pausedMaxProgress, currentProgress);
- pausedMinProgress = Math.min(pausedMinProgress, currentProgress);
- }
- }
- }
- if((numActive + numPaused) == 0)
- {
- this._dlActive = false;
- this._dlFinished = lastFinished;
- this.updateButton();
- this._lastTime = Infinity;
- return;
- }
- let dlPaused = (numActive == 0);
- let dlStatus = ((dlPaused) ? this._getters.strings.getString("pausedDownloads")
- : this._getters.strings.getString("activeDownloads"));
- let dlCount = ((dlPaused) ? numPaused : numActive);
- let dlTotalSize = ((dlPaused) ? pausedTotalSize : activeTotalSize);
- let dlTransferred = ((dlPaused) ? pausedTransferred : activeTransferred);
- let dlMaxProgress = ((dlPaused) ? pausedMaxProgress : activeMaxProgress);
- let dlMinProgress = ((dlPaused) ? pausedMinProgress : activeMinProgress);
- let dlProgressType = ((dlPaused) ? "paused" : "active");
- [this._dlTimeStr, this._lastTime] = DownloadUtils.getTimeLeft(maxTime, this._lastTime);
- this._dlCountStr = PluralForm.get(dlCount, dlStatus).replace("#1", dlCount);
- this._dlProgressAvg = ((dlTotalSize == 0) ? 100 : ((dlTransferred * 100) / dlTotalSize));
- this._dlProgressMax = ((dlTotalSize == 0) ? 100 : dlMaxProgress);
- this._dlProgressMin = ((dlTotalSize == 0) ? 100 : dlMinProgress);
- this._dlProgressType = dlProgressType + ((dlTotalSize == 0) ? "-unknown" : "");
- this._dlPaused = dlPaused;
- this._dlActive = true;
- this._dlFinished = false;
- this.updateButton();
- },
- updateButton: function()
- {
- let download_button = this._getters.downloadButton;
- let download_tooltip = this._getters.downloadButtonTooltip;
- let download_progress = this._getters.downloadButtonProgress;
- let download_label = this._getters.downloadButtonLabel;
- if(!download_button)
- {
- return;
- }
- if(!this._dlActive)
- {
- download_button.collapsed = true;
- download_label.value = download_tooltip.label = this._getters.strings.getString("noDownloads");
- download_progress.collapsed = true;
- download_progress.value = 0;
- if(this._dlFinished && this._handler.hasPBAPI && !this.isUIShowing)
- {
- this.callAttention(download_button);
- }
- return;
- }
- switch(this._service.downloadProgress)
- {
- case 2:
- download_progress.value = this._dlProgressMax;
- break;
- case 3:
- download_progress.value = this._dlProgressMin;
- break;
- default:
- download_progress.value = this._dlProgressAvg;
- break;
- }
- download_progress.setAttribute("pmType", this._dlProgressType);
- download_progress.collapsed = (this._service.downloadProgress == 0);
- download_label.value = this.buildString(this._service.downloadLabel);
- download_tooltip.label = this.buildString(this._service.downloadTooltip);
- this.clearAttention(download_button);
- download_button.collapsed = false;
- },
- callAttention: function(download_button)
- {
- if(this._dlNotifyGlowTimer != 0)
- {
- this._window.clearTimeout(this._dlNotifyGlowTimer);
- this._dlNotifyGlowTimer = 0;
- }
- download_button.setAttribute("attention", "true");
- if(this._service.downloadNotifyTimeout)
- {
- this._dlNotifyGlowTimer = this._window.setTimeout(function(self, button)
- {
- self._dlNotifyGlowTimer = 0;
- button.removeAttribute("attention");
- }, this._service.downloadNotifyTimeout, this, download_button);
- }
- },
- clearAttention: function(download_button)
- {
- if(this._dlNotifyGlowTimer != 0)
- {
- this._window.clearTimeout(this._dlNotifyGlowTimer);
- this._dlNotifyGlowTimer = 0;
- }
- download_button.removeAttribute("attention");
- },
- notify: function()
- {
- if(this._dlNotifyTimer == 0 && this._service.downloadNotifyAnimate)
- {
- let download_button_anchor = this._getters.downloadButtonAnchor;
- let download_notify_anchor = this._getters.downloadNotifyAnchor;
- if(download_button_anchor)
- {
- if(!download_notify_anchor.style.transform)
- {
- let bAnchorRect = download_button_anchor.getBoundingClientRect();
- let nAnchorRect = download_notify_anchor.getBoundingClientRect();
- let translateX = bAnchorRect.left - nAnchorRect.left;
- translateX += .5 * (bAnchorRect.width - nAnchorRect.width);
- let translateY = bAnchorRect.top - nAnchorRect.top;
- translateY += .5 * (bAnchorRect.height - nAnchorRect.height);
- download_notify_anchor.style.transform = "translate(" + translateX + "px, " + translateY + "px)";
- }
- download_notify_anchor.setAttribute("notification", "finish");
- this._dlNotifyTimer = this._window.setTimeout(function(self, anchor)
- {
- self._dlNotifyTimer = 0;
- anchor.removeAttribute("notification");
- anchor.style.transform = "";
- }, 1000, this, download_notify_anchor);
- }
- }
- },
- clearFinished: function()
- {
- this._dlFinished = false;
- let download_button = this._getters.downloadButton;
- if(download_button)
- {
- this.clearAttention(download_button);
- }
- },
- getAnchor: function(aCallback)
- {
- if(this._customizing)
- {
- aCallback(null);
- return;
- }
- aCallback(this._getters.downloadButtonAnchor);
- },
- openUI: function(aEvent)
- {
- this.clearFinished();
- switch(this._service.downloadButtonAction)
- {
- case 1: // Firefox Default
- this._handler.openUINative();
- break;
- case 2: // Show Library
- this._window.PlacesCommandHook.showPlacesOrganizer("Downloads");
- break;
- case 3: // Show Tab
- let found = this._gBrowser.browsers.some(function(browser, index)
- {
- if("about:downloads" == browser.currentURI.spec)
- {
- this._gBrowser.selectedTab = this._gBrowser.tabContainer.childNodes[index];
- return true;
- }
- }, this);
- if(!found)
- {
- this._window.openUILinkIn("about:downloads", "tab");
- }
- break;
- case 4: // External Command
- let command = this._service.downloadButtonActionCommand;
- if(commend)
- {
- this._window.goDoCommand(command);
- }
- break;
- default: // Nothing
- break;
- }
- aEvent.stopPropagation();
- },
- get isPrivateWindow()
- {
- return this._handler.hasPBAPI && PrivateBrowsingUtils.isWindowPrivate(this._window);
- },
- get isUIShowing()
- {
- switch(this._service.downloadButtonAction)
- {
- case 1: // Firefox Default
- return this._handler.isUIShowingNative;
- case 2: // Show Library
- var organizer = Services.wm.getMostRecentWindow("Places:Organizer");
- if(organizer)
- {
- let selectedNode = organizer.PlacesOrganizer._places.selectedNode;
- let downloadsItemId = organizer.PlacesUIUtils.leftPaneQueries["Downloads"];
- return selectedNode && selectedNode.itemId === downloadsItemId;
- }
- return false;
- case 3: // Show tab
- let currentURI = this._gBrowser.currentURI;
- return currentURI && currentURI.spec == "about:downloads";
- default: // Nothing
- return false;
- }
- },
- buildString: function(mode)
- {
- switch(mode)
- {
- case 0:
- return this._dlCountStr;
- case 1:
- return ((this._dlPaused) ? this._dlCountStr : this._dlTimeStr);
- default:
- let compStr = this._dlCountStr;
- if(!this._dlPaused)
- {
- compStr += " (" + this._dlTimeStr + ")";
- }
- return compStr;
- }
- }
- };
- function JSTransferHandler(window, downloadService)
- {
- this._window = window;
- let api = CU.import("resource://gre/modules/Downloads.jsm", {}).Downloads;
- this._activePublic = new JSTransferListener(downloadService, api.getList(api.PUBLIC), false);
- this._activePrivate = new JSTransferListener(downloadService, api.getList(api.PRIVATE), true);
- }
- JSTransferHandler.prototype =
- {
- _window: null,
- _activePublic: null,
- _activePrivate: null,
- destroy: function()
- {
- this._activePublic.destroy();
- this._activePrivate.destroy();
- ["_window", "_activePublic", "_activePrivate"].forEach(function(prop)
- {
- delete this[prop];
- }, this);
- },
- start: function()
- {
- this._activePublic.start();
- this._activePrivate.start();
- },
- stop: function()
- {
- this._activePublic.stop();
- this._activePrivate.stop();
- },
- get hasPBAPI()
- {
- return true;
- },
- openUINative: function()
- {
- this._window.DownloadsPanel.showPanel();
- },
- get isUIShowingNative()
- {
- return this._window.DownloadsPanel.isPanelShowing;
- },
- activeEntries: function()
- {
- return this._activePublic.downloads();
- },
- activePrivateEntries: function()
- {
- return this._activePrivate.downloads();
- }
- };
- function JSTransferListener(downloadService, listPromise, isPrivate)
- {
- this._downloadService = downloadService;
- this._isPrivate = isPrivate;
- this._downloads = new Map();
- listPromise.then(this.initList.bind(this)).then(null, CU.reportError);
- }
- JSTransferListener.prototype =
- {
- _downloadService: null,
- _list: null,
- _downloads: null,
- _isPrivate: false,
- _wantsStart: false,
- initList: function(list)
- {
- this._list = list;
- if(this._wantsStart) {
- this.start();
- }
- this._list.getAll().then(this.initDownloads.bind(this)).then(null, CU.reportError);
- },
- initDownloads: function(downloads)
- {
- downloads.forEach(function(download)
- {
- this.onDownloadAdded(download);
- }, this);
- },
- destroy: function()
- {
- this._downloads.clear();
- ["_downloadService", "_list", "_downloads"].forEach(function(prop)
- {
- delete this[prop];
- }, this);
- },
- start: function()
- {
- if(!this._list)
- {
- this._wantsStart = true;
- return;
- }
- this._list.addView(this);
- },
- stop: function()
- {
- if(!this._list)
- {
- this._wantsStart = false;
- return;
- }
- this._list.removeView(this);
- },
- downloads: function()
- {
- return this._downloads.values();
- },
- convertToState: function(dl)
- {
- if(dl.succeeded)
- {
- return CI.nsIDownloadManager.DOWNLOAD_FINISHED;
- }
- if(dl.error && dl.error.becauseBlockedByParentalControls)
- {
- return CI.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL;
- }
- if(dl.error)
- {
- return CI.nsIDownloadManager.DOWNLOAD_FAILED;
- }
- if(dl.canceled && dl.hasPartialData)
- {
- return CI.nsIDownloadManager.DOWNLOAD_PAUSED;
- }
- if(dl.canceled)
- {
- return CI.nsIDownloadManager.DOWNLOAD_CANCELED;
- }
- if(dl.stopped)
- {
- return CI.nsIDownloadManager.DOWNLOAD_NOTSTARTED;
- }
- return CI.nsIDownloadManager.DOWNLOAD_DOWNLOADING;
- },
- onDownloadAdded: function(aDownload)
- {
- let dl = this._downloads.get(aDownload);
- if(!dl)
- {
- dl = {};
- this._downloads.set(aDownload, dl);
- }
- dl.state = this.convertToState(aDownload);
- dl.size = aDownload.totalBytes;
- dl.speed = aDownload.speed;
- dl.transferred = aDownload.currentBytes;
- },
- onDownloadChanged: function(aDownload)
- {
- this.onDownloadAdded(aDownload);
- if(this._isPrivate != this._downloadService.isPrivateWindow)
- {
- return;
- }
- this._downloadService.updateStatus(aDownload.succeeded);
- if(aDownload.succeeded)
- {
- this._downloadService.notify()
- }
- },
- onDownloadRemoved: function(aDownload)
- {
- this._downloads.delete(aDownload);
- }
- };
|