1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/downloads/src/DownloadsTaskbar.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,180 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/** 1.11 + * Handles the download progress indicator in the taskbar. 1.12 + */ 1.13 + 1.14 +"use strict"; 1.15 + 1.16 +this.EXPORTED_SYMBOLS = [ 1.17 + "DownloadsTaskbar", 1.18 +]; 1.19 + 1.20 +//////////////////////////////////////////////////////////////////////////////// 1.21 +//// Globals 1.22 + 1.23 +const Cc = Components.classes; 1.24 +const Ci = Components.interfaces; 1.25 +const Cu = Components.utils; 1.26 +const Cr = Components.results; 1.27 + 1.28 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.29 + 1.30 +XPCOMUtils.defineLazyModuleGetter(this, "Downloads", 1.31 + "resource://gre/modules/Downloads.jsm"); 1.32 +XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", 1.33 + "resource:///modules/RecentWindow.jsm"); 1.34 +XPCOMUtils.defineLazyModuleGetter(this, "Services", 1.35 + "resource://gre/modules/Services.jsm"); 1.36 + 1.37 +XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () { 1.38 + if (!("@mozilla.org/windows-taskbar;1" in Cc)) { 1.39 + return null; 1.40 + } 1.41 + let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"] 1.42 + .getService(Ci.nsIWinTaskbar); 1.43 + return winTaskbar.available && winTaskbar; 1.44 +}); 1.45 + 1.46 +XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () { 1.47 + return ("@mozilla.org/widget/macdocksupport;1" in Cc) && 1.48 + Cc["@mozilla.org/widget/macdocksupport;1"] 1.49 + .getService(Ci.nsITaskbarProgress); 1.50 +}); 1.51 + 1.52 +//////////////////////////////////////////////////////////////////////////////// 1.53 +//// DownloadsTaskbar 1.54 + 1.55 +/** 1.56 + * Handles the download progress indicator in the taskbar. 1.57 + */ 1.58 +this.DownloadsTaskbar = { 1.59 + /** 1.60 + * Underlying DownloadSummary providing the aggregate download information, or 1.61 + * null if the indicator has never been initialized. 1.62 + */ 1.63 + _summary: null, 1.64 + 1.65 + /** 1.66 + * nsITaskbarProgress object to which download information is dispatched. 1.67 + * This can be null if the indicator has never been initialized or if the 1.68 + * indicator is currently hidden on Windows. 1.69 + */ 1.70 + _taskbarProgress: null, 1.71 + 1.72 + /** 1.73 + * This method is called after a new browser window is opened, and ensures 1.74 + * that the download progress indicator is displayed in the taskbar. 1.75 + * 1.76 + * On Windows, the indicator is attached to the first browser window that 1.77 + * calls this method. When the window is closed, the indicator is moved to 1.78 + * another browser window, if available, in no particular order. When there 1.79 + * are no browser windows visible, the indicator is hidden. 1.80 + * 1.81 + * On Mac OS X, the indicator is initialized globally when this method is 1.82 + * called for the first time. Subsequent calls have no effect. 1.83 + * 1.84 + * @param aBrowserWindow 1.85 + * nsIDOMWindow object of the newly opened browser window to which the 1.86 + * indicator may be attached. 1.87 + */ 1.88 + registerIndicator: function (aBrowserWindow) 1.89 + { 1.90 + if (!this._taskbarProgress) { 1.91 + if (gMacTaskbarProgress) { 1.92 + // On Mac OS X, we have to register the global indicator only once. 1.93 + this._taskbarProgress = gMacTaskbarProgress; 1.94 + // Free the XPCOM reference on shutdown, to prevent detecting a leak. 1.95 + Services.obs.addObserver(() => { 1.96 + this._taskbarProgress = null; 1.97 + gMacTaskbarProgress = null; 1.98 + }, "quit-application-granted", false); 1.99 + } else if (gWinTaskbar) { 1.100 + // On Windows, the indicator is currently hidden because we have no 1.101 + // previous browser window, thus we should attach the indicator now. 1.102 + this._attachIndicator(aBrowserWindow); 1.103 + } else { 1.104 + // The taskbar indicator is not available on this platform. 1.105 + return; 1.106 + } 1.107 + } 1.108 + 1.109 + // Ensure that the DownloadSummary object will be created asynchronously. 1.110 + if (!this._summary) { 1.111 + Downloads.getSummary(Downloads.ALL).then(summary => { 1.112 + // In case the method is re-entered, we simply ignore redundant 1.113 + // invocations of the callback, instead of keeping separate state. 1.114 + if (this._summary) { 1.115 + return; 1.116 + } 1.117 + this._summary = summary; 1.118 + return this._summary.addView(this); 1.119 + }).then(null, Cu.reportError); 1.120 + } 1.121 + }, 1.122 + 1.123 + /** 1.124 + * On Windows, attaches the taskbar indicator to the specified browser window. 1.125 + */ 1.126 + _attachIndicator: function (aWindow) 1.127 + { 1.128 + // Activate the indicator on the specified window. 1.129 + let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.130 + .getInterface(Ci.nsIWebNavigation) 1.131 + .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner 1.132 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.133 + .getInterface(Ci.nsIXULWindow).docShell; 1.134 + this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell); 1.135 + 1.136 + // If the DownloadSummary object has already been created, we should update 1.137 + // the state of the new indicator, otherwise it will be updated as soon as 1.138 + // the DownloadSummary view is registered. 1.139 + if (this._summary) { 1.140 + this.onSummaryChanged(); 1.141 + } 1.142 + 1.143 + aWindow.addEventListener("unload", () => { 1.144 + // Locate another browser window, excluding the one being closed. 1.145 + let browserWindow = RecentWindow.getMostRecentBrowserWindow(); 1.146 + if (browserWindow) { 1.147 + // Move the progress indicator to the other browser window. 1.148 + this._attachIndicator(browserWindow); 1.149 + } else { 1.150 + // The last browser window has been closed. We remove the reference to 1.151 + // the taskbar progress object so that the indicator will be registered 1.152 + // again on the next browser window that is opened. 1.153 + this._taskbarProgress = null; 1.154 + } 1.155 + }, false); 1.156 + }, 1.157 + 1.158 + ////////////////////////////////////////////////////////////////////////////// 1.159 + //// DownloadSummary view 1.160 + 1.161 + onSummaryChanged: function () 1.162 + { 1.163 + // If the last browser window has been closed, we have no indicator anymore. 1.164 + if (!this._taskbarProgress) { 1.165 + return; 1.166 + } 1.167 + 1.168 + if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) { 1.169 + this._taskbarProgress.setProgressState( 1.170 + Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0); 1.171 + } else { 1.172 + // For a brief moment before completion, some download components may 1.173 + // report more transferred bytes than the total number of bytes. Thus, 1.174 + // ensure that we never break the expectations of the progress indicator. 1.175 + let progressCurrentBytes = Math.min(this._summary.progressTotalBytes, 1.176 + this._summary.progressCurrentBytes); 1.177 + this._taskbarProgress.setProgressState( 1.178 + Ci.nsITaskbarProgress.STATE_NORMAL, 1.179 + progressCurrentBytes, 1.180 + this._summary.progressTotalBytes); 1.181 + } 1.182 + }, 1.183 +};