browser/components/downloads/src/DownloadsTaskbar.jsm

changeset 0
6474c204b198
     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 +};

mercurial