toolkit/mozapps/downloads/DownloadTaskbarProgress.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial