Wed, 31 Dec 2014 06:09:35 +0100
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 };