DownloadsTaskbar.jsm 6.7 KB

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