Bug 474060 - Show download progress in app icon in Windows 7 taskbar. Tests by Felipe Gomes. r=sdwilsh r=mconnor r=sid, sr=vlad, ui-r=faaborg
authorSiddharth Agarwal <sid.bugzilla@gmail.com>
Thu, 15 Apr 2010 00:14:12 +0200
changeset 40768 b45e2fd7ce4a82e6aa9fd7689be281ca5a526902
parent 40767 0b3c38dad8a7ea4ae7a3d33e143dc75c224318a1
child 40769 a085a040a6bdf7a8331bdbdd18e2ba1837ad97ef
push idunknown
push userunknown
push dateunknown
reviewerssdwilsh, mconnor, sid, vlad, faaborg
bugs474060
milestone1.9.3a5pre
Bug 474060 - Show download progress in app icon in Windows 7 taskbar. Tests by Felipe Gomes. r=sdwilsh r=mconnor r=sid, sr=vlad, ui-r=faaborg
browser/base/content/browser.js
toolkit/mozapps/downloads/DownloadTaskbarProgress.jsm
toolkit/mozapps/downloads/Makefile.in
toolkit/mozapps/downloads/content/downloads.js
toolkit/mozapps/downloads/tests/chrome/Makefile.in
toolkit/mozapps/downloads/tests/chrome/test_taskbarprogress_downloadstates.xul
toolkit/mozapps/downloads/tests/chrome/test_taskbarprogress_service.xul
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1336,16 +1336,23 @@ function delayedStartup(isLoadingBlank, 
   // If the user manually opens the download manager before the timeout, the
   // downloads will start right away, and getting the service again won't hurt.
   setTimeout(function() {
     gDownloadMgr = Cc["@mozilla.org/download-manager;1"].
                    getService(Ci.nsIDownloadManager);
 
     // Initialize the downloads monitor panel listener
     DownloadMonitorPanel.init();
+
+    if (Win7Features) {
+      let tempScope = {};
+      Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm",
+                tempScope);
+      tempScope.DownloadTaskbarProgress.onBrowserWindowLoad(window);
+    }
   }, 10000);
 
   // Delayed initialization of PlacesDBUtils.
   // This component checks for database coherence once per day, on
   // an idle timer, taking corrective actions where needed.
   setTimeout(function() PlacesUtils.startPlacesDBUtils(), 15000);
 
 #ifndef XP_MACOSX
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/downloads/DownloadTaskbarProgress.jsm
@@ -0,0 +1,394 @@
+/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 et filetype=javascript
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Windows Download Taskbar Progress.
+ *
+ * The Initial Developer of the Original Code is
+ * Siddharth Agarwal <sid.bugzilla@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+let EXPORTED_SYMBOLS = [
+  "DownloadTaskbarProgress",
+];
+
+////////////////////////////////////////////////////////////////////////////////
+//// Constants
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const kTaskbarID = "@mozilla.org/windows-taskbar;1";
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadTaskbarProgress Object
+
+const DownloadTaskbarProgress =
+{
+  /**
+   * Called when a browser window appears. This has an effect only when we
+   * don't already have an active window.
+   *
+   * @param aWindow
+   *        The browser window that we'll potentially use to display the
+   *        progress.
+   */
+  onBrowserWindowLoad: function DTP_onBrowserWindowLoad(aWindow)
+  {
+    if (!DownloadTaskbarProgressUpdater) {
+      return;
+    }
+    if (!DownloadTaskbarProgressUpdater._activeTaskbarProgress) {
+      DownloadTaskbarProgressUpdater._setActiveWindow(aWindow, false);
+    }
+  },
+
+  /**
+   * Called when the download window appears. The download window will take
+   * over as the active window.
+   */
+  onDownloadWindowLoad: function DTP_onDownloadWindowLoad(aWindow)
+  {
+    if (!DownloadTaskbarProgressUpdater) {
+      return;
+    }
+    DownloadTaskbarProgressUpdater._setActiveWindow(aWindow, true);
+  },
+
+  /**
+   * Getters for internal DownloadTaskbarProgressUpdater values
+   */
+
+  get activeTaskbarProgress() {
+    if (!DownloadTaskbarProgressUpdater) {
+      return null;
+    }
+    return DownloadTaskbarProgressUpdater._activeTaskbarProgress;
+  },
+
+  get activeWindowIsDownloadWindow() {
+    if (!DownloadTaskbarProgressUpdater) {
+      return null;
+    }
+    return DownloadTaskbarProgressUpdater._activeWindowIsDownloadWindow;
+  },
+
+  get taskbarState() {
+    if (!DownloadTaskbarProgressUpdater) {
+      return null;
+    }
+    return DownloadTaskbarProgressUpdater._taskbarState;
+  },
+
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadTaskbarProgressUpdater Object
+
+var DownloadTaskbarProgressUpdater =
+{
+  /// Reference to the taskbar.
+  _taskbar: null,
+
+  /// Reference to the download manager.
+  _dm: null,
+
+  /**
+   * Initialize and register ourselves as a download progress listener.
+   */
+  _init: function DTPU_init()
+  {
+    if (!(kTaskbarID in Cc)) {
+      // This means that the component isn't available
+      DownloadTaskbarProgressUpdater = null;
+      return;
+    }
+
+    this._taskbar = Cc[kTaskbarID].getService(Ci.nsIWinTaskbar);
+    if (!this._taskbar.available) {
+      // The Windows version is probably too old
+      DownloadTaskbarProgressUpdater = null;
+      return;
+    }
+
+    this._dm = Cc["@mozilla.org/download-manager;1"].
+               getService(Ci.nsIDownloadManager);
+    this._dm.addListener(this);
+
+    this._os = Cc["@mozilla.org/observer-service;1"].
+               getService(Ci.nsIObserverService);
+    this._os.addObserver(this, "quit-application-granted", false);
+
+    this._updateStatus();
+    // onBrowserWindowLoad/onDownloadWindowLoad are going to set the active
+    // window, so don't do it here.
+  },
+
+  /**
+   * Unregisters ourselves as a download progress listener.
+   */
+  _uninit: function DTPU_uninit() {
+    this._dm.removeListener(this);
+    this._os.removeObserver(this, "quit-application-granted");
+  },
+
+  /**
+   * This holds a reference to the taskbar progress for the window we're
+   * working with. This window would preferably be download window, but can be
+   * another window if it isn't open.
+   */
+  _activeTaskbarProgress: null,
+
+  /// Whether the active window is the download window
+  _activeWindowIsDownloadWindow: false,
+
+  /**
+   * Sets the active window, and whether it's the download window. This takes
+   * care of clearing out the previous active window's taskbar item, updating
+   * the taskbar, and setting an onunload listener.
+   *
+   * @param aWindow
+   *        The window to set as active.
+   * @param aIsDownloadWindow
+   *        Whether this window is a download window.
+   */
+  _setActiveWindow: function DTPU_setActiveWindow(aWindow, aIsDownloadWindow)
+  {
+    // Clear out the taskbar for the old active window. (If there was no active
+    // window, this is a no-op.)
+    this._clearTaskbar();
+
+    this._activeWindowIsDownloadWindow = aIsDownloadWindow;
+    if (aWindow) {
+      // Get the taskbar progress for this window
+      let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+                       getInterface(Ci.nsIWebNavigation).
+                       QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
+                       QueryInterface(Ci.nsIInterfaceRequestor).
+                       getInterface(Ci.nsIXULWindow).docShell;
+      let taskbarProgress = this._taskbar.getTaskbarProgress(docShell);
+      this._activeTaskbarProgress = taskbarProgress;
+
+      this._updateTaskbar();
+      // _onActiveWindowUnload is idempotent, so we don't need to check whether
+      // we've already set this before or not.
+      aWindow.addEventListener("unload", function () {
+        DownloadTaskbarProgressUpdater._onActiveWindowUnload(taskbarProgress);
+      }, false);
+    }
+    else {
+      this._activeTaskbarProgress = null;
+    }
+  },
+
+  /// Current state displayed on the active window's taskbar item
+  _taskbarState: Ci.nsITaskbarProgress.STATE_NO_PROGRESS,
+  _totalSize: 0,
+  _totalTransferred: 0,
+
+  /**
+   * Update the active window's taskbar indicator with the current state. There
+   * are two cases here:
+   * 1. If the active window is the download window, then we always update
+   *    the taskbar indicator.
+   * 2. If the active window isn't the download window, then we update only if
+   *    the status is Normal, i.e. one or more downloads are currently
+   *    progressing. If we aren't, then we clear the indicator.
+   */
+  _updateTaskbar: function DTPU_updateTaskbar()
+  {
+    if (!this._activeTaskbarProgress) {
+      return;
+    }
+
+    // If the active window is not the download manager window, set the state
+    // only if it is Normal
+    if (this._activeWindowIsDownloadWindow ||
+        (this._taskbarState == Ci.nsITaskbarProgress.STATE_NORMAL)) {
+      this._activeTaskbarProgress.setProgressState(this._taskbarState,
+                                                   this._totalTransferred,
+                                                   this._totalSize);
+    }
+    // Clear any state otherwise
+    else {
+      this._clearTaskbar();
+    }
+  },
+
+  /**
+   * Clear taskbar state. This is needed:
+   * - to transfer the indicator off a window before transferring it onto
+   *   another one
+   * - whenever we don't want to show it for a non-download window.
+   */
+  _clearTaskbar: function DTPU_clearTaskbar()
+  {
+    if (this._activeTaskbarProgress) {
+      this._activeTaskbarProgress.setProgressState(
+        Ci.nsITaskbarProgress.STATE_NO_PROGRESS
+      );
+    }
+  },
+
+  /**
+   * Update this._taskbarState, this._totalSize and this._totalTransferred.
+   * This is called when the download manager is initialized or when the
+   * progress or state of a download changes.
+   * We compute the number of active and paused downloads, and the total size
+   * and total amount already transferred across whichever downloads we have
+   * the data for.
+   * - If there are no active downloads, then we don't want to show any
+   *   progress.
+   * - If the number of active downloads is equal to the number of paused
+   *   downloads, then we show a paused indicator if we know the size of at
+   *   least one download, and no indicator if we don't.
+   * - If the number of active downloads is more than the number of paused
+   *   downloads, then we show a "normal" indicator if we know the size of at
+   *   least one download, and an indeterminate indicator if we don't.
+   */
+  _updateStatus: function DTPU_updateStatus()
+  {
+    let numActive = this._dm.activeDownloadCount;
+    let totalSize = 0, totalTransferred = 0;
+
+    if (numActive == 0) {
+      this._taskbarState = Ci.nsITaskbarProgress.STATE_NO_PROGRESS;
+    }
+    else {
+      let numPaused = 0;
+
+      // Enumerate all active downloads
+      let downloads = this._dm.activeDownloads;
+      while (downloads.hasMoreElements()) {
+        let download = downloads.getNext().QueryInterface(Ci.nsIDownload);
+        // Only set values if we actually know the download size
+        if (download.percentComplete < 100 && download.size > 0) {
+          totalSize += download.size;
+          totalTransferred += download.amountTransferred;
+        }
+        // We might need to display a paused state, so track this
+        if (download.state == this._dm.DOWNLOAD_PAUSED) {
+          numPaused++;
+        }
+      }
+
+      // If all downloads are paused, show the progress as paused, unless we
+      // don't have any information about sizes, in which case we don't
+      // display anything
+      if (numActive == numPaused) {
+        if (totalSize == 0) {
+          this._taskbarState = Ci.nsITaskbarProgress.STATE_NO_PROGRESS;
+          totalTransferred = 0;
+        }
+        else {
+          this._taskbarState = Ci.nsITaskbarProgress.STATE_PAUSED;
+        }
+      }
+      // If at least one download is not paused, and we don't have any
+      // information about download sizes, display an indeterminate indicator
+      else if (totalSize == 0) {
+        this._taskbarState = Ci.nsITaskbarProgress.STATE_INDETERMINATE;
+        totalTransferred = 0;
+      }
+      // Otherwise display a normal progress bar
+      else {
+        this._taskbarState = Ci.nsITaskbarProgress.STATE_NORMAL;
+      }
+    }
+
+    this._totalSize = totalSize;
+    this._totalTransferred = totalTransferred;
+  },
+
+  /**
+   * Called when a window that at one point has been an active window is
+   * closed. If this window is currently the active window, we need to look for
+   * another window and make that our active window.
+   *
+   * This function is idempotent, so multiple calls for the same window are not
+   * a problem.
+   *
+   * @param aTaskbarProgress
+   *        The taskbar progress for the window that is being unloaded.
+   */
+  _onActiveWindowUnload: function DTPU_onActiveWindowUnload(aTaskbarProgress)
+  {
+    if (this._activeTaskbarProgress == aTaskbarProgress) {
+      let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
+                           getService(Ci.nsIWindowMediator);
+      let windows = windowMediator.getEnumerator(null);
+      let newActiveWindow = null;
+      if (windows.hasMoreElements()) {
+        newActiveWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
+      }
+
+      // We aren't ever going to reach this point while the download manager is
+      // open, so it's safe to assume false for the second operand
+      this._setActiveWindow(newActiveWindow, false);
+    }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIDownloadProgressListener
+
+  /**
+   * Update status if a download's progress has changed.
+   */
+  onProgressChange: function DTPU_onProgressChange()
+  {
+    this._updateStatus();
+    this._updateTaskbar();
+  },
+
+  /**
+   * Update status if a download's state has changed.
+   */
+  onDownloadStateChange: function DTPU_onDownloadStateChange()
+  {
+    this._updateStatus();
+    this._updateTaskbar();
+  },
+
+  onSecurityChange: function() { },
+
+  onStateChange: function() { },
+
+  observe: function DTPU_observe(aSubject, aTopic, aData) {
+    if (aTopic == "quit-application-granted") {
+      this._uninit();
+    }
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Initialization
+
+DownloadTaskbarProgressUpdater._init();
--- a/toolkit/mozapps/downloads/Makefile.in
+++ b/toolkit/mozapps/downloads/Makefile.in
@@ -46,13 +46,19 @@ MODULE = helperAppDlg
 
 EXTRA_PP_COMPONENTS = nsHelperAppDlg.js
 
 EXTRA_JS_MODULES = \
   DownloadLastDir.jsm \
   DownloadUtils.jsm \
   $(NULL)
 
+ifeq ($(OS_ARCH),WINNT)
+EXTRA_JS_MODULES += \
+  DownloadTaskbarProgress.jsm \
+  $(NULL)
+endif
+
 ifdef ENABLE_TESTS
 DIRS += tests
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/mozapps/downloads/content/downloads.js
+++ b/toolkit/mozapps/downloads/content/downloads.js
@@ -479,16 +479,24 @@ function Startup()
   // Clear the search box and move focus to the list on escape from the box
   gSearchBox.addEventListener("keypress", function(e) {
     if (e.keyCode == e.DOM_VK_ESCAPE) {
       // Move focus to the list instead of closing the window
       gDownloadsView.focus();
       e.preventDefault();
     }
   }, false);
+
+#ifdef XP_WIN
+#ifndef WINCE
+  let tempScope = {};
+  Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", tempScope);
+  tempScope.DownloadTaskbarProgress.onDownloadWindowLoad(window);
+#endif
+#endif
 }
 
 function Shutdown()
 {
   gDownloadManager.removeListener(gDownloadListener);
 
   let obs = Cc["@mozilla.org/observer-service;1"].
             getService(Ci.nsIObserverService);
--- a/toolkit/mozapps/downloads/tests/chrome/Makefile.in
+++ b/toolkit/mozapps/downloads/tests/chrome/Makefile.in
@@ -77,10 +77,17 @@ include $(topsrcdir)/config/rules.mk
   utils.js \
   $(NULL)
 ifneq (,$(filter cocoa, $(MOZ_WIDGET_TOOLKIT)))
 _CHROME_FILES += \
   test_backspace_key_removes.xul \
   $(NULL)
 endif
 
+ifeq ($(OS_ARCH),WINNT)
+_CHROME_FILES += \
+  test_taskbarprogress_downloadstates.xul \
+  test_taskbarprogress_service.xul \
+  $(NULL)
+endif
+
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/downloads/tests/chrome/test_taskbarprogress_downloadstates.xul
@@ -0,0 +1,271 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Windows Download Taskbar Progress.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Felipe Gomes <felipc@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests that the Windows 7 Taskbar Progress is correctly updated when
+ * the download state changes.
+ */
+-->
+
+<window title="Win7 Taskbar Progress"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="test();">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/chrome/toolkit/mozapps/downloads/tests/chrome/utils.js"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+
+const kTaskbarID = "@mozilla.org/windows-taskbar;1";
+const DOWNLOAD_MANAGER_URL = "chrome://mozapps/content/downloads/downloads.xul";
+const DLMGR_UI_DONE = "download-manager-ui-done";
+
+const Cu = Components.utils;
+const nsITP = Ci.nsITaskbarProgress;
+
+let DownloadTaskbarProgress, dm;
+let downloadA, downloadB, gGen;
+
+let testState = {
+  activeDownloadCount: 0,
+  pausedDownloadCount: 0,
+  finishedDownloadCount: 0,
+
+  regularStateTested: false,
+  pausedStateTested: false,
+  noDownloadsStateTested: false
+}
+
+function continueTest() {
+  SimpleTest.executeSoon(function(){
+    gGen.next();
+  });
+}
+
+let downloadListener = {
+
+  onStateChange: function(a, b, c, d, e) {
+    checkCorrectState();
+  },
+
+  onDownloadStateChange: function(aState, aDownload)
+  {
+
+    if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_PAUSED) {
+      testState.pausedDownloadCount++;
+      testState.activeDownloadCount--;
+
+      continueTest();
+    }
+
+    if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
+      testState.activeDownloadCount--;
+      testState.finishedDownloadCount++;
+      aDownload.targetFile.remove(false);
+
+      SimpleTest.executeSoon(checkCorrectState);
+      if(testState.finishedDownloadCount == 2) {
+        SimpleTest.executeSoon(finishTest);
+      }
+
+    }
+
+    if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING) {
+        continueTest();
+    }
+
+  },
+
+  onProgressChange: function(a, b, c, d, e, f, g) { },
+  onSecurityChange: function(a, b, c, d) { }
+};
+
+function testSteps() {
+
+  // Step 1 - Add downloads and pause
+  testState.activeDownloadCount++;
+  downloadA = addDownload();
+  yield;
+
+  dm.pauseDownload(downloadA.id);
+  yield;
+
+  testState.activeDownloadCount++;
+  downloadB = addDownload();
+  yield;
+
+  dm.pauseDownload(downloadB.id);
+  yield;
+
+  // Step 2 - Resume downloads
+  testState.activeDownloadCount++;
+  testState.pausedDownloadCount--;
+  dm.resumeDownload(downloadA.id);
+  yield; 
+
+  testState.activeDownloadCount++;
+  testState.pausedDownloadCount--;
+  dm.resumeDownload(downloadB.id);
+  yield;
+
+  yield;
+}
+
+function finishTest() {
+  ok(testState.regularStateTested, "Tests went through regular download state");
+  ok(testState.pausedStateTested, "Tests went through paused download state");
+  ok(testState.noDownloadsStateTested, "Tests went through finished downloads state");
+  dm.removeListener(downloadListener);
+  gGen.close();
+  SimpleTest.finish();
+}
+
+function checkCorrectState() {
+
+  if(testState.activeDownloadCount < 0 || testState.pausedDownloadCount < 0) {
+    ok(false, "There shouldn't be negative download counts");
+    SimpleTest.finish();
+  }
+
+  let taskbarState = DownloadTaskbarProgress.taskbarState;
+
+  if (testState.activeDownloadCount) {
+    //There's at least one active download
+    ok(taskbarState == nsITP.STATE_NORMAL || taskbarState == nsITP.STATE_INDETERMINATE, "Correct downloading state");
+    testState.regularStateTested = true;
+  } else if (testState.pausedDownloadCount) {
+    //There are no active downloads but there are paused ones
+    ok(taskbarState == nsITP.STATE_PAUSED, "Correct paused state");
+    testState.pausedStateTested = true;
+  } else {
+    //No more downloads
+    ok(taskbarState == nsITP.STATE_NO_PROGRESS, "Correct finished downloads state");
+    testState.noDownloadsStateTested = true;
+  }
+
+}
+
+function test() {
+  testSetup();
+}
+
+function testSetup()
+{
+  //Test setup
+  let dmui = getDMUI();
+  if (!dmui) {
+    todo(false, "skip test for toolkit download manager UI");
+    return;
+  }
+
+  let isWin7OrHigher = false;
+  try {
+    let version = Cc["@mozilla.org/system-info;1"]
+                    .getService(Ci.nsIPropertyBag2)
+                    .getProperty("version");
+    isWin7OrHigher = (parseFloat(version) >= 6.1);
+  } catch (ex) { }
+
+  if (!isWin7OrHigher) {
+    return;
+  }
+
+  let tempScope = {};
+  Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", tempScope);
+  Cu.import("resource://gre/modules/Services.jsm");
+
+  DownloadTaskbarProgress = tempScope.DownloadTaskbarProgress;
+  let TaskbarService =  Cc[kTaskbarID].getService(Ci.nsIWinTaskbar);
+
+  isnot(DownloadTaskbarProgress, null, "Download taskbar progress service exists");
+  is(TaskbarService.available, true, "Taskbar Service is available");
+
+  dm = Cc["@mozilla.org/download-manager;1"].
+           getService(Ci.nsIDownloadManager);
+
+  // First, we clear out the database
+  dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+  // See if the DM is already open, and if it is, close it!
+  let win = Services.wm.getMostRecentWindow("Download:Manager");
+  if (win) {
+    win.close();
+  }
+  let os = Services.obs;
+  const DLMGR_UI_DONE = "download-manager-ui-done";
+
+  gGen = testSteps();
+  dm.addListener(downloadListener);
+
+  let testObs = {
+    observe: function(aSubject, aTopic, aData)
+    {
+      if (aTopic != DLMGR_UI_DONE) {
+        return;
+      }
+      os.removeObserver(testObs, DLMGR_UI_DONE);
+      continueTest();
+    }
+  };
+
+  // Register with the observer service
+  os.addObserver(testObs, DLMGR_UI_DONE, false);
+
+  // Show the Download Manager UI
+  dmui.show();
+
+  SimpleTest.waitForExplicitFinish();
+}
+
+  ]]>
+  </script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display:none;"></div>
+    <pre id="test"></pre>
+  </body>
+</window>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/downloads/tests/chrome/test_taskbarprogress_service.xul
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Windows Download Taskbar Progress.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Felipe Gomes <felipc@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests that the Windows 7 Taskbar Progress is correctly updated when
+ * windows are opened and closed.
+ */
+-->
+
+<window title="Win7 Taskbar Progress"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="test();">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/chrome/toolkit/mozapps/downloads/tests/chrome/utils.js"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+
+const kTaskbarID = "@mozilla.org/windows-taskbar;1";
+const DOWNLOAD_MANAGER_URL = "chrome://mozapps/content/downloads/downloads.xul";
+const DLMGR_UI_DONE = "download-manager-ui-done";
+
+let DownloadTaskbarProgress, TaskbarService, observerService, wwatch, chromeWindow;
+let gGen = null;
+
+function test() {
+  let isWin7OrHigher = false;
+  try {
+    let version = Cc["@mozilla.org/system-info;1"]
+                    .getService(Ci.nsIPropertyBag2)
+                    .getProperty("version");
+    isWin7OrHigher = (parseFloat(version) >= 6.1);
+  } catch (ex) { }
+
+  if (!isWin7OrHigher) {
+    return;
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  gGen = doTest();
+  gGen.next();
+}
+
+function continueTest() {
+  gGen.next();
+}
+
+function doTest() {
+
+  let tempScope = {};
+  Components.utils.import("resource://gre/modules/DownloadTaskbarProgress.jsm", tempScope);
+
+  DownloadTaskbarProgress = tempScope.DownloadTaskbarProgress;
+  TaskbarService =  Cc[kTaskbarID].getService(Ci.nsIWinTaskbar);
+
+  observerService = Cc["@mozilla.org/observer-service;1"].
+                      getService(Ci.nsIObserverService);
+
+  wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+             getService(Ci.nsIWindowWatcher);
+
+  isnot(DownloadTaskbarProgress, null, "Download taskbar progress service exists");
+  is(TaskbarService.available, true, "Taskbar Service is available");
+
+  let DMWindow = getDMWindow();
+  if (DMWindow)
+    DMWindow.close();
+
+  //Manually call onBrowserWindowLoad because this is delayed in 10sec
+  chromeWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+                   getInterface(Components.interfaces.nsIWebNavigation).
+                   QueryInterface(Components.interfaces.nsIDocShellTreeItem).
+                   rootTreeItem.
+                   QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+                   getInterface(Components.interfaces.nsIDOMWindow);
+
+  DownloadTaskbarProgress.onBrowserWindowLoad(chromeWindow);
+
+  is(DownloadTaskbarProgress.activeWindowIsDownloadWindow, false,
+     "DownloadTaskbarProgress window is not the Download window");
+
+  checkActiveTaskbar(false, window);
+  openDownloadManager(continueTest);
+  yield;
+
+  let DMWindow = getDMWindow();
+
+  ok(DMWindow, "Download Manager window was opened");
+  checkActiveTaskbar(true, DMWindow);
+
+  DMWindow.close();
+  setTimeout(continueTest, 100);
+  yield;
+
+  checkActiveTaskbar(false, window);
+
+  let browserWindow = openBrowserWindow(continueTest);
+  yield;
+
+  ok(browserWindow, "Browser window was opened");
+  DownloadTaskbarProgress.onBrowserWindowLoad(browserWindow);
+
+  // The owner window should not have changed, since our
+  // original window still exists
+  checkActiveTaskbar(false, window);
+
+  browserWindow.close();
+  SimpleTest.finish();
+  yield;
+}
+
+function checkActiveTaskbar(isDownloadManager, ownerWindow) {
+
+  isnot(DownloadTaskbarProgress.activeTaskbarProgress, null, "DownloadTaskbarProgress has an active taskbar");
+
+  is(DownloadTaskbarProgress.activeWindowIsDownloadWindow, isDownloadManager,
+     "The active taskbar progress " + (isDownloadManager ? "is" : "is not") + " the Download Manager");
+
+  if (ownerWindow) {
+    let ownerWindowDocShell = ownerWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+                                getInterface(Ci.nsIWebNavigation).
+                                QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
+                                QueryInterface(Ci.nsIInterfaceRequestor).
+                                getInterface(Ci.nsIXULWindow).docShell;
+
+    let windowTaskbarProgress = TaskbarService.getTaskbarProgress(ownerWindowDocShell);
+
+    is(DownloadTaskbarProgress.activeTaskbarProgress, windowTaskbarProgress,
+       "DownloadTaskbarProgress has the expected taskbar as active");
+  }
+
+}
+
+function openBrowserWindow(callback) {
+
+  let browserWindow = openDialog(chromeWindow.location, "_blank",
+                                 "chrome,all,dialog=no", "about:blank");
+ 
+  let helperFunc = function() {
+    callback();
+    browserWindow.removeEventListener("load", helperFunc, false);
+  }
+
+  browserWindow.addEventListener("load", helperFunc, false);
+
+  return browserWindow;
+}
+
+function openDownloadManager(callback) {
+
+  let testObs = {
+    observe: function(aSubject, aTopic, aData) {
+      if (aTopic != DLMGR_UI_DONE) {
+        return;
+      }
+
+      callback();
+      observerService.removeObserver(testObs, DLMGR_UI_DONE);
+    }
+  };
+
+  observerService.addObserver(testObs, DLMGR_UI_DONE, false);
+
+  Cc["@mozilla.org/download-manager-ui;1"].
+    getService(Ci.nsIDownloadManagerUI).show();
+
+}
+  ]]>
+  </script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display:none;"></div>
+    <pre id="test"></pre>
+  </body>
+</window>