toolkit/mozapps/downloads/DownloadTaskbarProgress.jsm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * vim: sw=2 ts=2 sts=2 et filetype=javascript
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 this.EXPORTED_SYMBOLS = [
michael@0 8 "DownloadTaskbarProgress",
michael@0 9 ];
michael@0 10
michael@0 11 ////////////////////////////////////////////////////////////////////////////////
michael@0 12 //// Constants
michael@0 13
michael@0 14 const Cc = Components.classes;
michael@0 15 const Ci = Components.interfaces;
michael@0 16 const Cu = Components.utils;
michael@0 17 const Cr = Components.results;
michael@0 18
michael@0 19 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 20 XPCOMUtils.defineLazyModuleGetter(this, "Services",
michael@0 21 "resource://gre/modules/Services.jsm");
michael@0 22
michael@0 23 const kTaskbarIDWin = "@mozilla.org/windows-taskbar;1";
michael@0 24 const kTaskbarIDMac = "@mozilla.org/widget/macdocksupport;1";
michael@0 25
michael@0 26 ////////////////////////////////////////////////////////////////////////////////
michael@0 27 //// DownloadTaskbarProgress Object
michael@0 28
michael@0 29 this.DownloadTaskbarProgress =
michael@0 30 {
michael@0 31 init: function DTP_init()
michael@0 32 {
michael@0 33 if (DownloadTaskbarProgressUpdater) {
michael@0 34 DownloadTaskbarProgressUpdater._init();
michael@0 35 }
michael@0 36 },
michael@0 37
michael@0 38 /**
michael@0 39 * Called when a browser window appears. This has an effect only when we
michael@0 40 * don't already have an active window.
michael@0 41 *
michael@0 42 * @param aWindow
michael@0 43 * The browser window that we'll potentially use to display the
michael@0 44 * progress.
michael@0 45 */
michael@0 46 onBrowserWindowLoad: function DTP_onBrowserWindowLoad(aWindow)
michael@0 47 {
michael@0 48 this.init();
michael@0 49 if (!DownloadTaskbarProgressUpdater) {
michael@0 50 return;
michael@0 51 }
michael@0 52 if (!DownloadTaskbarProgressUpdater._activeTaskbarProgress) {
michael@0 53 DownloadTaskbarProgressUpdater._setActiveWindow(aWindow, false);
michael@0 54 }
michael@0 55 },
michael@0 56
michael@0 57 /**
michael@0 58 * Called when the download window appears. The download window will take
michael@0 59 * over as the active window.
michael@0 60 */
michael@0 61 onDownloadWindowLoad: function DTP_onDownloadWindowLoad(aWindow)
michael@0 62 {
michael@0 63 if (!DownloadTaskbarProgressUpdater) {
michael@0 64 return;
michael@0 65 }
michael@0 66 DownloadTaskbarProgressUpdater._setActiveWindow(aWindow, true);
michael@0 67 },
michael@0 68
michael@0 69 /**
michael@0 70 * Getters for internal DownloadTaskbarProgressUpdater values
michael@0 71 */
michael@0 72
michael@0 73 get activeTaskbarProgress() {
michael@0 74 if (!DownloadTaskbarProgressUpdater) {
michael@0 75 return null;
michael@0 76 }
michael@0 77 return DownloadTaskbarProgressUpdater._activeTaskbarProgress;
michael@0 78 },
michael@0 79
michael@0 80 get activeWindowIsDownloadWindow() {
michael@0 81 if (!DownloadTaskbarProgressUpdater) {
michael@0 82 return null;
michael@0 83 }
michael@0 84 return DownloadTaskbarProgressUpdater._activeWindowIsDownloadWindow;
michael@0 85 },
michael@0 86
michael@0 87 get taskbarState() {
michael@0 88 if (!DownloadTaskbarProgressUpdater) {
michael@0 89 return null;
michael@0 90 }
michael@0 91 return DownloadTaskbarProgressUpdater._taskbarState;
michael@0 92 },
michael@0 93
michael@0 94 };
michael@0 95
michael@0 96 ////////////////////////////////////////////////////////////////////////////////
michael@0 97 //// DownloadTaskbarProgressUpdater Object
michael@0 98
michael@0 99 var DownloadTaskbarProgressUpdater =
michael@0 100 {
michael@0 101 /// Whether the taskbar is initialized.
michael@0 102 _initialized: false,
michael@0 103
michael@0 104 /// Reference to the taskbar.
michael@0 105 _taskbar: null,
michael@0 106
michael@0 107 /// Reference to the download manager.
michael@0 108 _dm: null,
michael@0 109
michael@0 110 /**
michael@0 111 * Initialize and register ourselves as a download progress listener.
michael@0 112 */
michael@0 113 _init: function DTPU_init()
michael@0 114 {
michael@0 115 if (this._initialized) {
michael@0 116 return; // Already initialized
michael@0 117 }
michael@0 118 this._initialized = true;
michael@0 119
michael@0 120 if (kTaskbarIDWin in Cc) {
michael@0 121 this._taskbar = Cc[kTaskbarIDWin].getService(Ci.nsIWinTaskbar);
michael@0 122 if (!this._taskbar.available) {
michael@0 123 // The Windows version is probably too old
michael@0 124 DownloadTaskbarProgressUpdater = null;
michael@0 125 return;
michael@0 126 }
michael@0 127 } else if (kTaskbarIDMac in Cc) {
michael@0 128 this._activeTaskbarProgress = Cc[kTaskbarIDMac].
michael@0 129 getService(Ci.nsITaskbarProgress);
michael@0 130 } else {
michael@0 131 DownloadTaskbarProgressUpdater = null;
michael@0 132 return;
michael@0 133 }
michael@0 134
michael@0 135 this._taskbarState = Ci.nsITaskbarProgress.STATE_NO_PROGRESS;
michael@0 136
michael@0 137 this._dm = Cc["@mozilla.org/download-manager;1"].
michael@0 138 getService(Ci.nsIDownloadManager);
michael@0 139 this._dm.addPrivacyAwareListener(this);
michael@0 140
michael@0 141 this._os = Cc["@mozilla.org/observer-service;1"].
michael@0 142 getService(Ci.nsIObserverService);
michael@0 143 this._os.addObserver(this, "quit-application-granted", false);
michael@0 144
michael@0 145 this._updateStatus();
michael@0 146 // onBrowserWindowLoad/onDownloadWindowLoad are going to set the active
michael@0 147 // window, so don't do it here.
michael@0 148 },
michael@0 149
michael@0 150 /**
michael@0 151 * Unregisters ourselves as a download progress listener.
michael@0 152 */
michael@0 153 _uninit: function DTPU_uninit() {
michael@0 154 this._dm.removeListener(this);
michael@0 155 this._os.removeObserver(this, "quit-application-granted");
michael@0 156 this._activeTaskbarProgress = null;
michael@0 157 this._initialized = false;
michael@0 158 },
michael@0 159
michael@0 160 /**
michael@0 161 * This holds a reference to the taskbar progress for the window we're
michael@0 162 * working with. This window would preferably be download window, but can be
michael@0 163 * another window if it isn't open.
michael@0 164 */
michael@0 165 _activeTaskbarProgress: null,
michael@0 166
michael@0 167 /// Whether the active window is the download window
michael@0 168 _activeWindowIsDownloadWindow: false,
michael@0 169
michael@0 170 /**
michael@0 171 * Sets the active window, and whether it's the download window. This takes
michael@0 172 * care of clearing out the previous active window's taskbar item, updating
michael@0 173 * the taskbar, and setting an onunload listener.
michael@0 174 *
michael@0 175 * @param aWindow
michael@0 176 * The window to set as active.
michael@0 177 * @param aIsDownloadWindow
michael@0 178 * Whether this window is a download window.
michael@0 179 */
michael@0 180 _setActiveWindow: function DTPU_setActiveWindow(aWindow, aIsDownloadWindow)
michael@0 181 {
michael@0 182 #ifdef XP_WIN
michael@0 183 // Clear out the taskbar for the old active window. (If there was no active
michael@0 184 // window, this is a no-op.)
michael@0 185 this._clearTaskbar();
michael@0 186
michael@0 187 this._activeWindowIsDownloadWindow = aIsDownloadWindow;
michael@0 188 if (aWindow) {
michael@0 189 // Get the taskbar progress for this window
michael@0 190 let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
michael@0 191 getInterface(Ci.nsIWebNavigation).
michael@0 192 QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
michael@0 193 QueryInterface(Ci.nsIInterfaceRequestor).
michael@0 194 getInterface(Ci.nsIXULWindow).docShell;
michael@0 195 let taskbarProgress = this._taskbar.getTaskbarProgress(docShell);
michael@0 196 this._activeTaskbarProgress = taskbarProgress;
michael@0 197
michael@0 198 this._updateTaskbar();
michael@0 199 // _onActiveWindowUnload is idempotent, so we don't need to check whether
michael@0 200 // we've already set this before or not.
michael@0 201 aWindow.addEventListener("unload", function () {
michael@0 202 DownloadTaskbarProgressUpdater._onActiveWindowUnload(taskbarProgress);
michael@0 203 }, false);
michael@0 204 }
michael@0 205 else {
michael@0 206 this._activeTaskbarProgress = null;
michael@0 207 }
michael@0 208 #endif
michael@0 209 },
michael@0 210
michael@0 211 /// Current state displayed on the active window's taskbar item
michael@0 212 _taskbarState: null,
michael@0 213 _totalSize: 0,
michael@0 214 _totalTransferred: 0,
michael@0 215
michael@0 216 _shouldSetState: function DTPU_shouldSetState()
michael@0 217 {
michael@0 218 #ifdef XP_WIN
michael@0 219 // If the active window is not the download manager window, set the state
michael@0 220 // only if it is normal or indeterminate.
michael@0 221 return this._activeWindowIsDownloadWindow ||
michael@0 222 (this._taskbarState == Ci.nsITaskbarProgress.STATE_NORMAL ||
michael@0 223 this._taskbarState == Ci.nsITaskbarProgress.STATE_INDETERMINATE);
michael@0 224 #else
michael@0 225 return true;
michael@0 226 #endif
michael@0 227 },
michael@0 228
michael@0 229 /**
michael@0 230 * Update the active window's taskbar indicator with the current state. There
michael@0 231 * are two cases here:
michael@0 232 * 1. If the active window is the download window, then we always update
michael@0 233 * the taskbar indicator.
michael@0 234 * 2. If the active window isn't the download window, then we update only if
michael@0 235 * the status is normal or indeterminate. i.e. one or more downloads are
michael@0 236 * currently progressing or in scan mode. If we aren't, then we clear the
michael@0 237 * indicator.
michael@0 238 */
michael@0 239 _updateTaskbar: function DTPU_updateTaskbar()
michael@0 240 {
michael@0 241 if (!this._activeTaskbarProgress) {
michael@0 242 return;
michael@0 243 }
michael@0 244
michael@0 245 if (this._shouldSetState()) {
michael@0 246 this._activeTaskbarProgress.setProgressState(this._taskbarState,
michael@0 247 this._totalTransferred,
michael@0 248 this._totalSize);
michael@0 249 }
michael@0 250 // Clear any state otherwise
michael@0 251 else {
michael@0 252 this._clearTaskbar();
michael@0 253 }
michael@0 254 },
michael@0 255
michael@0 256 /**
michael@0 257 * Clear taskbar state. This is needed:
michael@0 258 * - to transfer the indicator off a window before transferring it onto
michael@0 259 * another one
michael@0 260 * - whenever we don't want to show it for a non-download window.
michael@0 261 */
michael@0 262 _clearTaskbar: function DTPU_clearTaskbar()
michael@0 263 {
michael@0 264 if (this._activeTaskbarProgress) {
michael@0 265 this._activeTaskbarProgress.setProgressState(
michael@0 266 Ci.nsITaskbarProgress.STATE_NO_PROGRESS
michael@0 267 );
michael@0 268 }
michael@0 269 },
michael@0 270
michael@0 271 /**
michael@0 272 * Update this._taskbarState, this._totalSize and this._totalTransferred.
michael@0 273 * This is called when the download manager is initialized or when the
michael@0 274 * progress or state of a download changes.
michael@0 275 * We compute the number of active and paused downloads, and the total size
michael@0 276 * and total amount already transferred across whichever downloads we have
michael@0 277 * the data for.
michael@0 278 * - If there are no active downloads, then we don't want to show any
michael@0 279 * progress.
michael@0 280 * - If the number of active downloads is equal to the number of paused
michael@0 281 * downloads, then we show a paused indicator if we know the size of at
michael@0 282 * least one download, and no indicator if we don't.
michael@0 283 * - If the number of active downloads is more than the number of paused
michael@0 284 * downloads, then we show a "normal" indicator if we know the size of at
michael@0 285 * least one download, and an indeterminate indicator if we don't.
michael@0 286 */
michael@0 287 _updateStatus: function DTPU_updateStatus()
michael@0 288 {
michael@0 289 let numActive = this._dm.activeDownloadCount + this._dm.activePrivateDownloadCount;
michael@0 290 let totalSize = 0, totalTransferred = 0;
michael@0 291
michael@0 292 if (numActive == 0) {
michael@0 293 this._taskbarState = Ci.nsITaskbarProgress.STATE_NO_PROGRESS;
michael@0 294 }
michael@0 295 else {
michael@0 296 let numPaused = 0, numScanning = 0;
michael@0 297
michael@0 298 // Enumerate all active downloads
michael@0 299 [this._dm.activeDownloads, this._dm.activePrivateDownloads].forEach(function(downloads) {
michael@0 300 while (downloads.hasMoreElements()) {
michael@0 301 let download = downloads.getNext().QueryInterface(Ci.nsIDownload);
michael@0 302 // Only set values if we actually know the download size
michael@0 303 if (download.percentComplete != -1) {
michael@0 304 totalSize += download.size;
michael@0 305 totalTransferred += download.amountTransferred;
michael@0 306 }
michael@0 307 // We might need to display a paused state, so track this
michael@0 308 if (download.state == this._dm.DOWNLOAD_PAUSED) {
michael@0 309 numPaused++;
michael@0 310 } else if (download.state == this._dm.DOWNLOAD_SCANNING) {
michael@0 311 numScanning++;
michael@0 312 }
michael@0 313 }
michael@0 314 }.bind(this));
michael@0 315
michael@0 316 // If all downloads are paused, show the progress as paused, unless we
michael@0 317 // don't have any information about sizes, in which case we don't
michael@0 318 // display anything
michael@0 319 if (numActive == numPaused) {
michael@0 320 if (totalSize == 0) {
michael@0 321 this._taskbarState = Ci.nsITaskbarProgress.STATE_NO_PROGRESS;
michael@0 322 totalTransferred = 0;
michael@0 323 }
michael@0 324 else {
michael@0 325 this._taskbarState = Ci.nsITaskbarProgress.STATE_PAUSED;
michael@0 326 }
michael@0 327 }
michael@0 328 // If at least one download is not paused, and we don't have any
michael@0 329 // information about download sizes, display an indeterminate indicator
michael@0 330 else if (totalSize == 0 || numActive == numScanning) {
michael@0 331 this._taskbarState = Ci.nsITaskbarProgress.STATE_INDETERMINATE;
michael@0 332 totalSize = 0;
michael@0 333 totalTransferred = 0;
michael@0 334 }
michael@0 335 // Otherwise display a normal progress bar
michael@0 336 else {
michael@0 337 this._taskbarState = Ci.nsITaskbarProgress.STATE_NORMAL;
michael@0 338 }
michael@0 339 }
michael@0 340
michael@0 341 this._totalSize = totalSize;
michael@0 342 this._totalTransferred = totalTransferred;
michael@0 343 },
michael@0 344
michael@0 345 /**
michael@0 346 * Called when a window that at one point has been an active window is
michael@0 347 * closed. If this window is currently the active window, we need to look for
michael@0 348 * another window and make that our active window.
michael@0 349 *
michael@0 350 * This function is idempotent, so multiple calls for the same window are not
michael@0 351 * a problem.
michael@0 352 *
michael@0 353 * @param aTaskbarProgress
michael@0 354 * The taskbar progress for the window that is being unloaded.
michael@0 355 */
michael@0 356 _onActiveWindowUnload: function DTPU_onActiveWindowUnload(aTaskbarProgress)
michael@0 357 {
michael@0 358 if (this._activeTaskbarProgress == aTaskbarProgress) {
michael@0 359 let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
michael@0 360 getService(Ci.nsIWindowMediator);
michael@0 361 let windows = windowMediator.getEnumerator(null);
michael@0 362 let newActiveWindow = null;
michael@0 363 if (windows.hasMoreElements()) {
michael@0 364 newActiveWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
michael@0 365 }
michael@0 366
michael@0 367 // We aren't ever going to reach this point while the download manager is
michael@0 368 // open, so it's safe to assume false for the second operand
michael@0 369 this._setActiveWindow(newActiveWindow, false);
michael@0 370 }
michael@0 371 },
michael@0 372
michael@0 373 //////////////////////////////////////////////////////////////////////////////
michael@0 374 //// nsIDownloadProgressListener
michael@0 375
michael@0 376 /**
michael@0 377 * Update status if a download's progress has changed.
michael@0 378 */
michael@0 379 onProgressChange: function DTPU_onProgressChange()
michael@0 380 {
michael@0 381 this._updateStatus();
michael@0 382 this._updateTaskbar();
michael@0 383 },
michael@0 384
michael@0 385 /**
michael@0 386 * Update status if a download's state has changed.
michael@0 387 */
michael@0 388 onDownloadStateChange: function DTPU_onDownloadStateChange()
michael@0 389 {
michael@0 390 this._updateStatus();
michael@0 391 this._updateTaskbar();
michael@0 392 },
michael@0 393
michael@0 394 onSecurityChange: function() { },
michael@0 395
michael@0 396 onStateChange: function() { },
michael@0 397
michael@0 398 observe: function DTPU_observe(aSubject, aTopic, aData) {
michael@0 399 if (aTopic == "quit-application-granted") {
michael@0 400 this._uninit();
michael@0 401 }
michael@0 402 }
michael@0 403 };

mercurial