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: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80 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 /**
8 * Handles the download progress indicator in the taskbar.
9 */
11 "use strict";
13 this.EXPORTED_SYMBOLS = [
14 "DownloadsTaskbar",
15 ];
17 ////////////////////////////////////////////////////////////////////////////////
18 //// Globals
20 const Cc = Components.classes;
21 const Ci = Components.interfaces;
22 const Cu = Components.utils;
23 const Cr = Components.results;
25 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
27 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
28 "resource://gre/modules/Downloads.jsm");
29 XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
30 "resource:///modules/RecentWindow.jsm");
31 XPCOMUtils.defineLazyModuleGetter(this, "Services",
32 "resource://gre/modules/Services.jsm");
34 XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () {
35 if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
36 return null;
37 }
38 let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
39 .getService(Ci.nsIWinTaskbar);
40 return winTaskbar.available && winTaskbar;
41 });
43 XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () {
44 return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
45 Cc["@mozilla.org/widget/macdocksupport;1"]
46 .getService(Ci.nsITaskbarProgress);
47 });
49 ////////////////////////////////////////////////////////////////////////////////
50 //// DownloadsTaskbar
52 /**
53 * Handles the download progress indicator in the taskbar.
54 */
55 this.DownloadsTaskbar = {
56 /**
57 * Underlying DownloadSummary providing the aggregate download information, or
58 * null if the indicator has never been initialized.
59 */
60 _summary: null,
62 /**
63 * nsITaskbarProgress object to which download information is dispatched.
64 * This can be null if the indicator has never been initialized or if the
65 * indicator is currently hidden on Windows.
66 */
67 _taskbarProgress: null,
69 /**
70 * This method is called after a new browser window is opened, and ensures
71 * that the download progress indicator is displayed in the taskbar.
72 *
73 * On Windows, the indicator is attached to the first browser window that
74 * calls this method. When the window is closed, the indicator is moved to
75 * another browser window, if available, in no particular order. When there
76 * are no browser windows visible, the indicator is hidden.
77 *
78 * On Mac OS X, the indicator is initialized globally when this method is
79 * called for the first time. Subsequent calls have no effect.
80 *
81 * @param aBrowserWindow
82 * nsIDOMWindow object of the newly opened browser window to which the
83 * indicator may be attached.
84 */
85 registerIndicator: function (aBrowserWindow)
86 {
87 if (!this._taskbarProgress) {
88 if (gMacTaskbarProgress) {
89 // On Mac OS X, we have to register the global indicator only once.
90 this._taskbarProgress = gMacTaskbarProgress;
91 // Free the XPCOM reference on shutdown, to prevent detecting a leak.
92 Services.obs.addObserver(() => {
93 this._taskbarProgress = null;
94 gMacTaskbarProgress = null;
95 }, "quit-application-granted", false);
96 } else if (gWinTaskbar) {
97 // On Windows, the indicator is currently hidden because we have no
98 // previous browser window, thus we should attach the indicator now.
99 this._attachIndicator(aBrowserWindow);
100 } else {
101 // The taskbar indicator is not available on this platform.
102 return;
103 }
104 }
106 // Ensure that the DownloadSummary object will be created asynchronously.
107 if (!this._summary) {
108 Downloads.getSummary(Downloads.ALL).then(summary => {
109 // In case the method is re-entered, we simply ignore redundant
110 // invocations of the callback, instead of keeping separate state.
111 if (this._summary) {
112 return;
113 }
114 this._summary = summary;
115 return this._summary.addView(this);
116 }).then(null, Cu.reportError);
117 }
118 },
120 /**
121 * On Windows, attaches the taskbar indicator to the specified browser window.
122 */
123 _attachIndicator: function (aWindow)
124 {
125 // Activate the indicator on the specified window.
126 let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
127 .getInterface(Ci.nsIWebNavigation)
128 .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
129 .QueryInterface(Ci.nsIInterfaceRequestor)
130 .getInterface(Ci.nsIXULWindow).docShell;
131 this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
133 // If the DownloadSummary object has already been created, we should update
134 // the state of the new indicator, otherwise it will be updated as soon as
135 // the DownloadSummary view is registered.
136 if (this._summary) {
137 this.onSummaryChanged();
138 }
140 aWindow.addEventListener("unload", () => {
141 // Locate another browser window, excluding the one being closed.
142 let browserWindow = RecentWindow.getMostRecentBrowserWindow();
143 if (browserWindow) {
144 // Move the progress indicator to the other browser window.
145 this._attachIndicator(browserWindow);
146 } else {
147 // The last browser window has been closed. We remove the reference to
148 // the taskbar progress object so that the indicator will be registered
149 // again on the next browser window that is opened.
150 this._taskbarProgress = null;
151 }
152 }, false);
153 },
155 //////////////////////////////////////////////////////////////////////////////
156 //// DownloadSummary view
158 onSummaryChanged: function ()
159 {
160 // If the last browser window has been closed, we have no indicator anymore.
161 if (!this._taskbarProgress) {
162 return;
163 }
165 if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
166 this._taskbarProgress.setProgressState(
167 Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
168 } else {
169 // For a brief moment before completion, some download components may
170 // report more transferred bytes than the total number of bytes. Thus,
171 // ensure that we never break the expectations of the progress indicator.
172 let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
173 this._summary.progressCurrentBytes);
174 this._taskbarProgress.setProgressState(
175 Ci.nsITaskbarProgress.STATE_NORMAL,
176 progressCurrentBytes,
177 this._summary.progressTotalBytes);
178 }
179 },
180 };