|
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/. */ |
|
6 |
|
7 /** |
|
8 * Handles the download progress indicator in the taskbar. |
|
9 */ |
|
10 |
|
11 "use strict"; |
|
12 |
|
13 this.EXPORTED_SYMBOLS = [ |
|
14 "DownloadsTaskbar", |
|
15 ]; |
|
16 |
|
17 //////////////////////////////////////////////////////////////////////////////// |
|
18 //// Globals |
|
19 |
|
20 const Cc = Components.classes; |
|
21 const Ci = Components.interfaces; |
|
22 const Cu = Components.utils; |
|
23 const Cr = Components.results; |
|
24 |
|
25 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
26 |
|
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"); |
|
33 |
|
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 }); |
|
42 |
|
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 }); |
|
48 |
|
49 //////////////////////////////////////////////////////////////////////////////// |
|
50 //// DownloadsTaskbar |
|
51 |
|
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, |
|
61 |
|
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, |
|
68 |
|
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 } |
|
105 |
|
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 }, |
|
119 |
|
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); |
|
132 |
|
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 } |
|
139 |
|
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 }, |
|
154 |
|
155 ////////////////////////////////////////////////////////////////////////////// |
|
156 //// DownloadSummary view |
|
157 |
|
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 } |
|
164 |
|
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 }; |