Bug 391870 - Have the download manager store the referring uri when appropriate (and use it in the front end). r=mano, r=cbiesinger, sr=cbiesinger
authorsdwilsh@shawnwilsher.com
Fri, 17 Aug 2007 16:05:26 -0700
changeset 4765 efb8ffa885f97fd44a6da9670d80ffb3f120c722
parent 4764 a2b95187929bc707b5b8ac2a71033011d8b9570a
child 4766 93d4ec144028866d629f28f691ddffd94a848d22
push idunknown
push userunknown
push dateunknown
reviewersmano, cbiesinger, cbiesinger
bugs391870
milestone1.9a8pre
Bug 391870 - Have the download manager store the referring uri when appropriate (and use it in the front end). r=mano, r=cbiesinger, sr=cbiesinger
embedding/components/ui/progressDlg/Makefile.in
embedding/components/ui/progressDlg/nsIProgressDialog.idl
toolkit/components/downloads/public/Makefile.in
toolkit/components/downloads/public/nsIDownload.idl
toolkit/components/downloads/src/nsDownloadManager.cpp
toolkit/components/downloads/src/nsDownloadManager.h
toolkit/components/downloads/test/schema_migration/test_migration_to_2.js
toolkit/components/downloads/test/schema_migration/test_migration_to_3.js
toolkit/components/downloads/test/schema_migration/v2.sqlite
toolkit/mozapps/downloads/content/DownloadProgressListener.js
toolkit/mozapps/downloads/content/downloads.js
toolkit/mozapps/downloads/content/downloads.xul
uriloader/base/Makefile.in
uriloader/base/nsIDownload.idl
widget/src/windows/nsDragService.cpp
xpfe/components/download-manager/public/Makefile.in
xpfe/components/download-manager/public/nsIDownload.idl
--- a/embedding/components/ui/progressDlg/Makefile.in
+++ b/embedding/components/ui/progressDlg/Makefile.in
@@ -40,16 +40,12 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE 		= progressDlg
 XPIDL_MODULE 	= progressDlg
 
-XPIDLSRCS = 	\
-		nsIProgressDialog.idl \
-		$(NULL)
-
 EXTRA_COMPONENTS = nsProgressDialog.js
 
 include $(topsrcdir)/config/rules.mk
 
deleted file mode 100644
--- a/embedding/components/ui/progressDlg/nsIProgressDialog.idl
+++ /dev/null
@@ -1,100 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Bill Law    law@netscape.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 ***** */
-
-#include "nsIDownload.idl"
-
-interface nsIDOMWindow;
-interface nsIObserver;
-
-/* nsIProgressDialog
- *
- * These objects are used to display progress notifications to the user.
- * They are displayed while files are being saved to disk, for example.
- * 
- * Typical usage is to:
- *   1. Create an instance, via the component manager CreateInstance()
- *      method.
- *   2. Set appropriate attributes that control the display and behavior
- *      of the dialog.
- *   3. Open the dialog.
- *   4. Send progress notifications to the dialog, via its
- *      nsIDownloadProgressListener methods.
- *   5. Close the dialog when the operation completes, or when the user
- *      closes it manually.
- *   6. Release the instance.  The instance will be referenced by
- *      the dialog itself, so it won't get freed until the dialog closes.
- *      The dialog will keep the instance alive, so typically one does
- *      not need to hold a reference to it.
- */
-
-[scriptable, uuid(20e790a2-76c6-462d-851a-22ab6cbbe48b)]
-interface nsIProgressDialog : nsIDownload {
-   /**
-    * Open the dialog
-    *
-    * @param aParent  Parent window; optional (if null, then
-    *                 a top-level window is created)
-    */
-   void open( in nsIDOMWindow aParent );
-   
-   /**
-    * Whether the download should be cancelled when the progress
-    * dialog is closed using the standard OS close box.  This is
-    * useful for showing the progress dialog as an information
-    * window, which is what download manager does.
-    */
-   attribute PRBool cancelDownloadOnClose;
-
-   /**
-    * Observer for this dialog. If set, receives the following topics:
-    *   oncancel - observer should cancel the transfer
-    *   onpause  - observer should suspend the transfer
-    *   onresume - observer should resume the suspended transfer
-    * For each of those, the subject will be the nsIProgressDialog.
-    */
-   attribute nsIObserver observer;
-
-  /**
-   * The dialog object itself.  This might be null if the dialog isn't
-   * open yet, or has been closed.
-   */   
-   attribute nsIDOMWindow dialog;
-};
-
-
--- a/toolkit/components/downloads/public/Makefile.in
+++ b/toolkit/components/downloads/public/Makefile.in
@@ -41,12 +41,13 @@ srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE    = downloads
 
 XPIDLSRCS = nsIDownloadManager.idl \
             nsIDownloadProgressListener.idl \
+            nsIDownload.idl \
             $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
--- a/toolkit/components/downloads/public/nsIDownload.idl
+++ b/toolkit/components/downloads/public/nsIDownload.idl
@@ -49,17 +49,17 @@ interface nsIMIMEInfo;
  * Represents a download object.
  *
  * @note This object is no longer updated once it enters a completed state.
  *       Completed states are the following:
  *       nsIDownloadManager::DOWNLOAD_FINISHED
  *       nsIDownloadManager::DOWNLOAD_FAILED
  *       nsIDownloadManager::DOWNLOAD_CANCELED
  */
-[scriptable, uuid(974db2c6-fbd2-4de1-8d24-f54ce4f3e8bc)]
+[scriptable, uuid(e5900f21-7a8a-4f9f-ac67-5379a65c18b6)]
 interface nsIDownload : nsITransfer {
     
     /**
      * The target of a download is always a file on the local file system.
      */
     readonly attribute nsILocalFile targetFile;
 
     /**
@@ -122,15 +122,21 @@ interface nsIDownload : nsITransfer {
      */
     readonly attribute unsigned long id;
 
     /**
      * The state of the download.
      * @see nsIDownloadManager and nsIXPInstallManagerUI
      */
     readonly attribute short state;
+
+    /**
+     * The referrer uri of the download.  This is only valid for HTTP downloads,
+     * and can be null.
+     */
+    readonly attribute nsIURI referrer;
 };
 
 %{C++
 // {E3FA9D0A-1DD1-11B2-BDEF-8C720B597445}
 #define NS_DOWNLOAD_CID \
     { 0xe3fa9d0a, 0x1dd1, 0x11b2, { 0xbd, 0xef, 0x8c, 0x72, 0x0b, 0x59, 0x74, 0x45 } }
 %}
--- a/toolkit/components/downloads/src/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/src/nsDownloadManager.cpp
@@ -61,16 +61,18 @@
 #include "nsEnumeratorUtils.h"
 #include "nsIFileURL.h"
 #include "nsEmbedCID.h"
 #include "mozStorageCID.h"
 #include "mozIStorageService.h"
 #include "mozStorageHelper.h"
 #include "nsIMutableArray.h"
 #include "nsIAlertsService.h"
+#include "nsIHttpChannel.h"
+#include "nsIPropertyBag2.h"
 
 #ifdef XP_WIN
 #include <shlobj.h>
 #endif
 
 static PRBool gStoppingDownloads = PR_FALSE;
 
 #define DOWNLOAD_MANAGER_FE_URL "chrome://mozapps/content/downloads/downloads.xul"
@@ -83,17 +85,17 @@ static PRBool gStoppingDownloads = PR_FA
 #define PREF_BDM_SHOWWHENSTARTING "browser.download.manager.showWhenStarting"
 #define PREF_BDM_FOCUSWHENSTARTING "browser.download.manager.focusWhenStarting"
 #define PREF_BDM_CLOSEWHENDONE "browser.download.manager.closeWhenDone"
 #define PREF_BDM_FLASHCOUNT "browser.download.manager.flashCount"
 #define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
 
 static const PRInt64 gUpdateInterval = 400 * PR_USEC_PER_MSEC;
 
-#define DM_SCHEMA_VERSION      2
+#define DM_SCHEMA_VERSION      3
 #define DM_DB_NAME             NS_LITERAL_STRING("downloads.sqlite")
 #define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt")
 
 ///////////////////////////////////////////////////////////////////////////////
 // nsDownloadManager
 
 NS_IMPL_ISUPPORTS2(nsDownloadManager, nsIDownloadManager, nsIObserver)
 
@@ -269,16 +271,30 @@ nsDownloadManager::InitDB(PRBool *aDoImp
 
       // Finally, update the schemaVersion variable and the database schema
       schemaVersion = 2;
       rv = mDBConn->SetSchemaVersion(schemaVersion);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     // Fallthrough to the next upgrade
 
+  case 2: // Add referrer column to the database
+    {
+      rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+        "ALTER TABLE moz_downloads "
+        "ADD COLUMN referrer TEXT"));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // Finally, update the schemaVersion variable and the database schema
+      schemaVersion = 3;
+      rv = mDBConn->SetSchemaVersion(schemaVersion);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    // Fallthrough to the next upgrade
+
   case DM_SCHEMA_VERSION:
     break;
 
   case 0:
     {
       NS_WARNING("Could not get download database's schema version!");
 
       // The table may still be usable - someone may have just messed with the
@@ -335,17 +351,18 @@ nsDownloadManager::CreateTable()
   return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE moz_downloads ("
       "id INTEGER PRIMARY KEY, "
       "name TEXT, "
       "source TEXT, "
       "target TEXT, "
       "startTime INTEGER, "
       "endTime INTEGER, "
-      "state INTEGER"
+      "state INTEGER, "
+      "referrer TEXT"
     ")"));
 }
 
 nsresult
 nsDownloadManager::ImportDownloadHistory()
 {
   nsCOMPtr<nsIFile> dlFile;
   nsresult rv = NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE,
@@ -550,37 +567,36 @@ nsDownloadManager::Init()
   nsCOMPtr<nsIStringBundleService> bundleService =
     do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   
   rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE,
                                    getter_AddRefs(mBundle));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+    "UPDATE moz_downloads "
+    "SET startTime = ?1, endTime = ?2, state = ?3, referrer = ?4 "
+    "WHERE id = ?5"), getter_AddRefs(mUpdateDownloadStatement));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // The following three AddObserver calls must be the last lines in this function,
   // because otherwise, this function may fail (and thus, this object would be not
   // completely initialized), but the observerservice would still keep a reference
   // to us and notify us about shutdown, which may cause crashes.
   // failure to add an observer is not critical
   //
   // These observers will be cleaned up automatically at app shutdown.  We do
   // not bother explicitly breaking the observers because we are a singleton
   // that lives for the duration of the app.
   //
   mObserverService->AddObserver(this, "quit-application", PR_FALSE);
   mObserverService->AddObserver(this, "quit-application-requested", PR_FALSE);
   mObserverService->AddObserver(this, "offline-requested", PR_FALSE);
 
-  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-    "UPDATE moz_downloads "
-    "SET name = ?1, source = ?2, target = ?3, startTime = ?4, endTime = ?5,"
-    "state = ?6 "
-    "WHERE id = ?7"), getter_AddRefs(mUpdateDownloadStatement));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   return NS_OK;
 }
 
 PRInt32 
 nsDownloadManager::GetRetentionBehavior()
 {
   // We use 0 as the default, which is "remove when done"
   nsresult rv;
@@ -598,17 +614,17 @@ nsresult
 nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
 {
   NS_ASSERTION(!FindDownload(aID),
                "If it is a current download, you should not call this method!");
 
   // First, let's query the database and see if it even exists
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-    "SELECT id, state, startTime, source, target, name "
+    "SELECT id, state, startTime, source, target, name, referrer "
     "FROM moz_downloads "
     "WHERE id = ?1"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->BindInt64Parameter(0, aID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRBool hasResults = PR_FALSE;
@@ -634,16 +650,23 @@ nsDownloadManager::GetDownloadFromDB(PRU
 
   nsCString target;
   stmt->GetUTF8String(4, target);
   rv = NS_NewURI(getter_AddRefs(dl->mTarget), target);
   NS_ENSURE_SUCCESS(rv, rv);
 
   stmt->GetString(5, dl->mDisplayName);
 
+  nsCString referrer;
+  rv = stmt->GetUTF8String(6, referrer);
+  if (NS_SUCCEEDED(rv) && !referrer.IsEmpty()) {
+    rv = NS_NewURI(getter_AddRefs(dl->mReferrer), referrer);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   nsCOMPtr<nsILocalFile> file;
   rv = dl->GetTargetFile(getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRBool fileExists;
   if (NS_SUCCEEDED(file->Exists(&fileExists)) && fileExists) {
     if (dl->mDownloadState == nsIDownloadManager::DOWNLOAD_FINISHED) {
       dl->mPercentComplete = 100;
@@ -811,23 +834,16 @@ nsDownloadManager::CancelDownload(PRUint
     if (exists)
       dl->mTempFile->Remove(PR_FALSE);
   }
 
   nsresult rv = FinishDownload(dl, nsIDownloadManager::DOWNLOAD_CANCELED,
                                "dl-cancel");
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // if there's a progress dialog open for the item,
-  // we have to notify it that we're cancelling
-  if (dl->mDialog) {
-    nsCOMPtr<nsIObserver> observer = do_QueryInterface(dl->mDialog);
-    observer->Observe(dl, "oncancel", nsnull);
-  }
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::RetryDownload(PRUint32 aID)
 {
   nsRefPtr<nsDownload> dl;
   nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
@@ -1173,22 +1189,18 @@ nsDownloadManager::Observe(nsISupports *
   nsresult rv;
   if (strcmp(aTopic, "oncancel") == 0) {
     nsCOMPtr<nsIDownload> dl = do_QueryInterface(aSubject, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PRUint32 id;
     dl->GetId(&id);
     nsDownload *dl2 = FindDownload(id);
-    if (dl2) {
-      // unset dialog since it's closing
-      dl2->mDialog = nsnull;
-      
+    if (dl2)
       return CancelDownload(id);  
-    }
   } else if (strcmp(aTopic, "quit-application") == 0) {
     gStoppingDownloads = PR_TRUE;
     
     if (currDownloadCount)
       CancelAllDownloads();
 
     // Now that active downloads have been canceled, remove all downloads if 
     // the user's retention policy specifies it. 
@@ -1348,17 +1360,47 @@ nsDownload::OnProgressChange64(nsIWebPro
                                PRInt64 aMaxSelfProgress,
                                PRInt64 aCurTotalProgress,
                                PRInt64 aMaxTotalProgress)
 {
   if (!mRequest)
     mRequest = aRequest; // used for pause/resume
 
   if (mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED) {
-    nsresult rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
+    // Obtain the referrer
+    nsresult rv;
+    nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+    if (channel) {
+      // first by trying to get the property
+      nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(channel));
+      if (props) {
+        // We have to check for a property on a property bag because the
+        // referrer may be empty for security reasons (for example, when loading
+        // an http page with an https referrer).
+        rv = props->GetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
+                                           NS_GET_IID(nsIURI),
+                                           getter_AddRefs(mReferrer));
+        if (NS_FAILED(rv))
+          mReferrer = nsnull;
+      }
+
+      // if that didn't work, we can still try to get the referrer from the
+      // nsIHttpChannel (if we can QI to it)
+      if (!mReferrer) {
+        nsCOMPtr<nsIHttpChannel> chan = do_QueryInterface(aRequest);
+        if (chan) {
+          rv = chan->GetReferrer(getter_AddRefs(mReferrer));
+          if (NS_FAILED(rv))
+            mReferrer = nsnull;
+        }
+      }
+    }
+
+    // Update the state and the database
+    rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
     NS_ENSURE_SUCCESS(rv, rv);
     mDownloadManager->mObserverService->NotifyObservers(this, "dl-start", nsnull);
   }
 
   // filter notifications since they come in so frequently
   PRTime now = PR_Now();
   PRIntervalTime delta = now - mLastUpdate;
   if (delta < gUpdateInterval)
@@ -1708,16 +1750,24 @@ nsDownload::GetSpeed(double* aSpeed)
 NS_IMETHODIMP
 nsDownload::GetId(PRUint32 *aId)
 {
   *aId = mID;
 
   return NS_OK;
 }
 
+NS_IMETHODIMP 
+nsDownload::GetReferrer(nsIURI **referrer)
+{
+  NS_IF_ADDREF(*referrer = mReferrer);
+
+  return NS_OK;
+}
+
 nsresult
 nsDownload::PauseResume(PRBool aPause)
 {
   if (mPaused == aPause || !mRequest)
     return NS_OK;
 
   if (aPause) {
     nsresult rv = mRequest->Suspend();
@@ -1735,44 +1785,37 @@ nsDownload::PauseResume(PRBool aPause)
 nsresult
 nsDownload::UpdateDB()
 {
   NS_ASSERTION(mID, "Download ID is stored as zero.  This is bad!");
   NS_ASSERTION(mDownloadManager, "Egads!  We have no download manager!");
 
   mozIStorageStatement *stmt = mDownloadManager->mUpdateDownloadStatement;
 
-  // name
-  nsresult rv = stmt->BindStringParameter(0, mDisplayName);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // source
-  nsCAutoString src;
-  rv = mSource->GetSpec(src);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = stmt->BindUTF8StringParameter(1, src);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // target
-  nsCAutoString target;
-  rv = mTarget->GetSpec(target);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = stmt->BindUTF8StringParameter(2, target);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // startTime
-  rv = stmt->BindInt64Parameter(3, mStartTime);
+  nsresult rv = stmt->BindInt64Parameter(0, mStartTime);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // endTime
-  rv = stmt->BindInt64Parameter(4, mLastUpdate);
+  rv = stmt->BindInt64Parameter(1, mLastUpdate);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // state
-  rv = stmt->BindInt32Parameter(5, mDownloadState);
+  rv = stmt->BindInt32Parameter(2, mDownloadState);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // referrer
+  if (mReferrer) {
+    nsCAutoString referrer;
+    rv = mReferrer->GetSpec(referrer);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = stmt->BindUTF8StringParameter(3, referrer);
+  } else {
+    rv = stmt->BindNullParameter(3);
+  }
   NS_ENSURE_SUCCESS(rv, rv);
 
   // id
-  rv = stmt->BindInt64Parameter(6, mID);
+  rv = stmt->BindInt64Parameter(4, mID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return stmt->Execute();
 }
--- a/toolkit/components/downloads/src/nsDownloadManager.h
+++ b/toolkit/components/downloads/src/nsDownloadManager.h
@@ -51,31 +51,29 @@
 #include "nsIURI.h"
 #include "nsIWebBrowserPersist.h"
 #include "nsILocalFile.h"
 #include "nsIRequest.h"
 #include "nsIObserver.h"
 #include "nsString.h"
 #include "nsIStringBundle.h"
 #include "nsISupportsPrimitives.h"
-#include "nsIProgressDialog.h"
 #include "nsIMIMEInfo.h"
 #include "nsITimer.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
 #include "nsISupportsArray.h"
 #include "nsCOMArray.h"
 #include "nsArrayEnumerator.h"
 #include "nsAutoPtr.h"
 #include "nsIObserverService.h"
 
 typedef PRInt16 DownloadState;
 typedef PRInt16 DownloadType;
 
-class nsXPIProgressListener;
 class nsDownload;
 
 class nsDownloadManager : public nsIDownloadManager,
                           public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOWNLOADMANAGER
@@ -226,19 +224,19 @@ protected:
 
   nsDownloadManager* mDownloadManager;
   nsCOMPtr<nsIURI> mTarget;
 
 private:
   nsString mDisplayName;
 
   nsCOMPtr<nsIURI> mSource;
+  nsCOMPtr<nsIURI> mReferrer;
   nsCOMPtr<nsICancelable> mCancelable;
   nsCOMPtr<nsIRequest> mRequest;
-  nsCOMPtr<nsIProgressDialog> mDialog;
   nsCOMPtr<nsILocalFile> mTempFile;
   nsCOMPtr<nsIMIMEInfo> mMIMEInfo;
   
   DownloadState mDownloadState;
   DownloadType  mDownloadType;
 
   PRUint32 mID;
   PRInt32 mPercentComplete;
--- a/toolkit/components/downloads/test/schema_migration/test_migration_to_2.js
+++ b/toolkit/components/downloads/test/schema_migration/test_migration_to_2.js
@@ -44,17 +44,17 @@ function run_test()
   // ok, now it is OK to init the download manager - this will perform the
   // migration!
   var dm = Cc["@mozilla.org/download-manager;1"].
            getService(Ci.nsIDownloadManager);
   var dbConn = dm.DBConnection;
   var stmt = null;
 
   // check schema version
-  do_check_eq(2, dbConn.schemaVersion);
+  do_check_true(dbConn.schemaVersion >= 2);
 
   // Check that the column no longer exists
   try {
     // throws when it doesn't exist
     stmt = dbConn.createStatement("SELECT iconURL FROM moz_downloads");
     do_throw("should not get here");
   } catch (e) {
     do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/downloads/test/schema_migration/test_migration_to_3.js
@@ -0,0 +1,77 @@
+/* ***** 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 Download Manager Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * 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 devaring the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not devare
+ * 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 file tests migration from v1 to v2
+
+function run_test()
+{
+  // First import the downloads.sqlite file
+  importDatabaseFile("v2.sqlite");
+
+  // ok, now it is OK to init the download manager - this will perform the
+  // migration!
+  var dm = Cc["@mozilla.org/download-manager;1"].
+           getService(Ci.nsIDownloadManager);
+  var dbConn = dm.DBConnection;
+  var stmt = null;
+
+  // check schema version
+  do_check_true(dbConn.schemaVersion >= 3);
+
+  // Check that the column exists (statement should not throw)
+  stmt = dbConn.createStatement("SELECT referrer FROM moz_downloads");
+
+  // now we check the entries
+  stmt = dbConn.createStatement(
+    "SELECT name, source, target, startTime, endTime, state, referrer " +
+    "FROM moz_downloads " +
+    "WHERE id = 2");
+  stmt.executeStep();
+  do_check_eq("381603.patch", stmt.getString(0));
+  do_check_eq("https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+              stmt.getUTF8String(1));
+  do_check_eq("file:///Users/sdwilsh/Desktop/381603.patch",
+              stmt.getUTF8String(2));
+  do_check_eq(1180493839859230, stmt.getInt64(3));
+  do_check_eq(1180493839859230, stmt.getInt64(4));
+  do_check_eq(1, stmt.getInt32(5));
+  do_check_true(stmt.getIsNull(6));
+  stmt.reset();
+
+  cleanup();
+}
+
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4b4d615362182d249b8ee37b457f3314402f3e76
GIT binary patch
literal 24576
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
RI1Wx7M$^&g<NSQ){|B1_fGGd~
--- a/toolkit/mozapps/downloads/content/DownloadProgressListener.js
+++ b/toolkit/mozapps/downloads/content/DownloadProgressListener.js
@@ -71,24 +71,26 @@ DownloadProgressListener.prototype =
       case Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING:
         // if dl is non-null, the download is already added to the UI, so we
         // just make sure it is where it is supposed to be
         if (!dl) {
           // We have to create the download object
           let uri = Cc["@mozilla.org/network/util;1"].
                     getService(Ci.nsIIOService).
                     newFileURI(aDownload.targetFile);
+          let referrer = aDownload.referrer;
           dl = createDownloadItem(aDownload.id,
                                   uri.spec,
                                   aDownload.displayName,
                                   aDownload.source.spec,
                                   aDownload.state,
                                   "",
                                   aDownload.percentComplete,
-                                  Math.round(aDownload.startTime / 1000));
+                                  Math.round(aDownload.startTime / 1000),
+                                  referrer ? referrer.spec : null);
         }
         gDownloadsView.insertBefore(dl, gDownloadsActiveTitle.nextSibling);
         break;
       case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
       case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
         downloadCompleted(aDownload);
         break;
       case Ci.nsIDownloadManager.DOWNLOAD_FINISHED:
@@ -122,21 +124,23 @@ DownloadProgressListener.prototype =
   onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
                               aCurTotalProgress, aMaxTotalProgress, aDownload)
   {
     var download = getDownload(aDownload.id);
     if (!download) {
       // d'oh - why this happens is complicated, let's just add it in
       let uri = Cc["@mozilla.org/network/util;1"].
                 getService(Ci.nsIIOService).newFileURI(aDownload.targetFile);
+      let referrer = aDownload.referrer;
       let itm = createDownloadItem(aDownload.id, uri.spec,
                                    aDownload.displayName,
                                    aDownload.source.spec,
                                    aDownload.state,
-                                   aDownload.percentComplete);
+                                   aDownload.percentComplete,
+                                   referrer ? referrer.spec : null);
       download = gDownloadsView.insertBefore(itm, gDownloadsActiveTitle.nextSibling);
     }
 
     // any activity means we should have active downloads!
     gDownloadsActiveTitle.hidden = false;
 
     // Update this download's progressmeter
     if (aDownload.percentComplete == -1)
--- a/toolkit/mozapps/downloads/content/downloads.js
+++ b/toolkit/mozapps/downloads/content/downloads.js
@@ -77,17 +77,17 @@ function fireEventForElement(aElement, a
 {
   var e = document.createEvent("Events");
   e.initEvent("download-" + aEventType, true, true);
   
   aElement.dispatchEvent(e);
 }
 
 function createDownloadItem(aID, aFile, aTarget, aURI, aState,
-                            aStatus, aProgress, aStartTime)
+                            aStatus, aProgress, aStartTime, aReferrer)
 {
   var dl = document.createElement("richlistitem");
   dl.setAttribute("type", "download");
   dl.setAttribute("id", "dl" + aID);
   dl.setAttribute("dlid", aID);
   dl.setAttribute("image", "moz-icon://" + aFile + "?size=32");
   dl.setAttribute("file", aFile);
   dl.setAttribute("target", aTarget);
@@ -96,17 +96,20 @@ function createDownloadItem(aID, aFile, 
   dl.setAttribute("status", aStatus);
   dl.setAttribute("progress", aProgress);
   dl.setAttribute("startTime", aStartTime);
 
   var ioSvc = Cc["@mozilla.org/network/io-service;1"].
               getService(Ci.nsIIOService);
   var file = ioSvc.newURI(aFile, null, null).QueryInterface(Ci.nsIFileURL).file;
   dl.setAttribute("path", file.nativePath || file.path);
-  
+
+  if (aReferrer)
+    dl.setAttribute("referrer", aReferrer);
+
   return dl;
 }
 
 function getDownload(aID)
 {
   return document.getElementById("dl" + aID);
 }
 
@@ -296,16 +299,27 @@ function openDownload(aDownload)
     f.launch();
   } catch (ex) {
     // if launch fails, try sending it through the system's external
     // file: URL handler
     openExternal(f);
   }
 }
 
+function openReferrer(aDownload)
+{
+  var uriString;
+  if (aDownload.hasAttribute("referrer"))
+    uriString = aDownload.getAttribute("referrer");
+  else
+    uriString = aDownload.getAttribute("uri");
+
+  openURL(uriString);
+}
+
 function showDownloadInfo(aDownload)
 {
   gUserInteracted = true;
 
   var popupTitle    = document.getElementById("information-title");
   var uriLabel      = document.getElementById("information-uri");
   var locationLabel = document.getElementById("information-location");
 
@@ -318,17 +332,21 @@ function showDownloadInfo(aDownload)
                                    dts.timeFormatNoSeconds,
                                    dateStarted.getFullYear(),
                                    dateStarted.getMonth() + 1,
                                    dateStarted.getDate(),
                                    dateStarted.getHours(),
                                    dateStarted.getMinutes(), 0);
   popupTitle.setAttribute("value", dateStarted);
   // Add proper uri and path
-  var uri = aDownload.getAttribute("uri");
+  var uri = "beltzner";
+  if (aDownload.hasAttribute("referrer"))
+    uri = aDownload.getAttribute("referrer");
+  else
+    uri = aDownload.getAttribute("uri");
   uriLabel.label = uri;
   uriLabel.setAttribute("tooltiptext", uri);
   var path = aDownload.getAttribute("path");
   locationLabel.label = path;
   locationLabel.setAttribute("tooltiptext", path);
 
   var button = document.getAnonymousElementByAttribute(aDownload, "anonid", "info");
   gDownloadInfoPopup.openPopup(button, "after_end", 0, 0, false, false);
@@ -512,16 +530,17 @@ var gDownloadViewController = {
         let file = getLocalFileFromNativePathOrUrl(dl.getAttribute("file"));
         return dl.openable && file.exists();
       case "cmd_pause":
         return dl.inProgress && !dl.paused;
       case "cmd_pauseResume":
         return dl.inProgress || dl.paused;
       case "cmd_resume":
         return dl.paused;
+      case "cmd_openReferrer":
       case "cmd_remove":
       case "cmd_retry":
         return dl.removable;
       case "cmd_showInfo":
         return true;
     }
     return false;
   },
@@ -549,16 +568,19 @@ var gDownloadViewController = {
   
   commands: {
     cmd_cancel: function(aSelectedItem) {
       cancelDownload(aSelectedItem);
     },
     cmd_open: function(aSelectedItem) {
       openDownload(aSelectedItem);
     },
+    cmd_openReferrer: function(aSelectedItem) {
+      openReferrer(aSelectedItem);
+    },
     cmd_pause: function(aSelectedItem) {
       pauseDownload(aSelectedItem);
     },
     cmd_pauseResume: function(aSelectedItem) {
       if (aSelectedItem.inProgress)
         this.commands.cmd_pause(aSelectedItem);
       else
         this.commands.cmd_resume(aSelectedItem);
@@ -621,17 +643,17 @@ function buildDefaultView()
 }
 
 /**
  * Builds the downloads list with a given statement and reference node.
  *
  * @param aStmt
  *        The compiled SQL statement to build with.  This needs to have the
  *        following columns in this order to work properly:
- *        id, target, name, source, state, startTime
+ *        id, target, name, source, state, startTime, referrer
  *        This statement should be ordered on the endTime ASC so that the end
  *        result is a list of downloads with their end time's descending.
  * @param aRef
  *        The node we use for placement of the download objects.  We place each
  *        new node above the previously inserted one.
  */
 function buildDownloadList(aStmt, aRef)
 {
@@ -649,17 +671,18 @@ function buildDownloadList(aStmt, aRef)
       // proper percentage complete for.  This download will actually be in
       // the active downloads array internally, so calling getDownload is cheap.
       let dl = gDownloadManager.getDownload(id);
       percentComplete = dl.percentComplete;
     }
     let dl = createDownloadItem(id, aStmt.getString(1),
                                 aStmt.getString(2), aStmt.getString(3),
                                 state, "", percentComplete,
-                                Math.round(aStmt.getInt64(5) / 1000));
+                                Math.round(aStmt.getInt64(5) / 1000),
+                                aStmt.getString(6));
     gDownloadsView.insertBefore(dl, aRef.nextSibling);
   }
 }
 
 var gActiveDownloadsQuery = null;
 function buildActiveDownloadsList()
 {
   // Are there any active downloads?
@@ -669,17 +692,18 @@ function buildActiveDownloadsList()
   // unhide the label
   gDownloadsActiveTitle.hidden = false;
 
   // repopulate the list
   var db = gDownloadManager.DBConnection;
   var stmt = gActiveDownloadsQuery;
   if (!stmt) {
     stmt = gActiveDownloadsQuery =
-      db.createStatement("SELECT id, target, name, source, state, startTime " +
+      db.createStatement("SELECT id, target, name, source, state, startTime, " +
+                         "referrer " +
                          "FROM moz_downloads " +
                          "WHERE state = ?1 " +
                          "OR state = ?2 " +
                          "OR state = ?3 " +
                          "ORDER BY endTime ASC");
   }
 
   try {
@@ -701,17 +725,18 @@ function buildActiveDownloadsList()
  */
 var gDownloadListWithTimeQuery = null;
 function buildDownloadListWithTime(aTime)
 {
   var db = gDownloadManager.DBConnection;
   var stmt = gDownloadListWithTimeQuery;
   if (!stmt) {
     stmt = gDownloadListWithTimeQuery =
-      db.createStatement("SELECT id, target, name, source, state, startTime " +
+      db.createStatement("SELECT id, target, name, source, state, startTime, " +
+                         "referrer " +
                          "FROM moz_downloads " +
                          "WHERE startTime >= ?1 " +
                          "AND (state = ?2 " +
                          "OR state = ?3 " +
                          "OR state = ?4) " +
                          "ORDER BY endTime ASC");
   }
 
@@ -745,17 +770,17 @@ function buildDownloadListWithSearch(aTe
   aTerms = aTerms.replace(/^\s+|\s+$/, "");
   if (aTerms.length == 0) {
     gSearching = false;
     gDownloadsOtherLabel.value = gDownloadsOtherLabel.getAttribute("completedlabel");
     buildDefaultView();
     return;
   }
 
-  var sql = "SELECT id, target, name, source, state, startTime " +
+  var sql = "SELECT id, target, name, source, state, startTime, referrer " +
             "FROM moz_downloads WHERE name LIKE ?1 ESCAPE '/' " +
             "AND state != ?2 AND state != ?3 ORDER BY endTime ASC";
 
   var db = gDownloadManager.DBConnection;
   var stmt = db.createStatement(sql);
 
   try {
     var paramForLike = stmt.escapeStringForLIKE(aTerms, '/');
--- a/toolkit/mozapps/downloads/content/downloads.xul
+++ b/toolkit/mozapps/downloads/content/downloads.xul
@@ -77,16 +77,18 @@
 
   <commandset id="downloadsCommands"
               commandupdater="true"
               oncommandupdate="gDownloadViewController.onCommandUpdate();">
     <command id="cmd_cancel"
              oncommand="gDownloadViewController.doCommand('cmd_cancel');"/>
     <command id="cmd_open"
              oncommand="gDownloadViewController.doCommand('cmd_open');"/>
+    <command id="cmd_openReferrer"
+             oncommand="gDownloadViewController.doCommand('cmd_openReferrer');"/>
     <command id="cmd_pause"
              oncommand="gDownloadViewController.doCommand('cmd_pause');"/>
     <command id="cmd_pauseResume"
              oncommand="gDownloadViewController.doCommand('cmd_pauseResume');"/>
     <command id="cmd_remove"
              oncommand="gDownloadViewController.doCommand('cmd_remove');"/>
     <command id="cmd_resume"
              oncommand="gDownloadViewController.doCommand('cmd_resume');"/>
@@ -144,17 +146,18 @@
               command="cmd_remove"/>
   </vbox>
   
   <menupopup id="downloadContextMenu" onpopupshowing="return buildContextMenu(event);"/>
 
   <!--Information popup-->
   <panel id="information" orient="vertical" align="start">
     <label id="information-title" flex="1"/>
-    <button type="image" crop="center" id="information-uri" class="mini-button" flex="1"/>
+    <button type="image" crop="center" id="information-uri" class="mini-button"
+            flex="1" command="cmd_openReferrer"/>
     <button type="image" crop="center" id="information-location" class="mini-button" flex="1" command="cmd_show"/>
   </panel>
 
   <richlistbox id="downloadView" flex="1" context="downloadContextMenu"
                ondragover="nsDragAndDrop.dragOver(event, gDownloadDNDObserver);"
                ondragdrop="nsDragAndDrop.drop(event, gDownloadDNDObserver);">
     <hbox id="active-downloads-title" align="center" hidden="true">
       <label id="active-downloads" value="&activeDownloads.label;"/>
--- a/uriloader/base/Makefile.in
+++ b/uriloader/base/Makefile.in
@@ -75,17 +75,16 @@ SDK_XPIDLSRCS = 						\
 				nsIWebProgress.idl 	\
 				nsIWebProgressListener.idl 	\
 				$(NULL)
 
 XPIDLSRCS = 							\
 				nsIContentHandler.idl 		\
 				nsIURILoader.idl	 	\
 				nsCURILoader.idl		\
-				nsIDownload.idl   \
 				nsITransfer.idl \
 				nsIDocumentLoader.idl		\
 				nsIWebProgressListener2.idl \
 				$(NULL)
 
 EXPORTS = 							\
 				nsURILoader.h	 		\
 				nsDocLoader.h			\
deleted file mode 100644
--- a/uriloader/base/nsIDownload.idl
+++ /dev/null
@@ -1,136 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Blake Ross <blaker@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
-
-#include "nsITransfer.idl"
-
-interface nsIURI;
-interface nsILocalFile;
-interface nsIObserver;
-interface nsICancelable;
-interface nsIWebProgressListener;
-interface nsIMIMEInfo;
-
-/**
- * Represents a download object.
- *
- * @note This object is no longer updated once it enters a completed state.
- *       Completed states are the following:
- *       nsIDownloadManager::DOWNLOAD_FINISHED
- *       nsIDownloadManager::DOWNLOAD_FAILED
- *       nsIDownloadManager::DOWNLOAD_CANCELED
- */
-[scriptable, uuid(974db2c6-fbd2-4de1-8d24-f54ce4f3e8bc)]
-interface nsIDownload : nsITransfer {
-    
-    /**
-     * The target of a download is always a file on the local file system.
-     */
-    readonly attribute nsILocalFile targetFile;
-
-    /**
-     * The percentage of transfer completed.
-     * If the file size is unknown it'll be -1 here.
-     */
-    readonly attribute PRInt32 percentComplete;
-
-    /**
-     * The amount of bytes downloaded so far.
-     */
-    readonly attribute PRUint64 amountTransferred;
-
-    /**
-     * The size of file in bytes.
-     * Unknown size is represented by LL_MAXUINT.
-     */
-    readonly attribute PRUint64 size;
-    
-    /**
-     * The source of the transfer.
-     */
-    readonly attribute nsIURI source;
-    
-    /**
-     * The target of the transfer.
-     */
-    readonly attribute nsIURI target;
- 
-    /**
-     * Object that can be used to cancel the download.
-     * Will be null after the download is finished.
-     */
-    readonly attribute nsICancelable cancelable;
-
-    /**
-     * The user-readable description of the transfer.
-     */
-    readonly attribute AString displayName;
-
-    /**
-     * The time a transfer was started.
-     */
-    readonly attribute long long startTime;
-
-    /**
-     * The speed of the transfer in bytes/sec.
-     */
-    readonly attribute double speed;
-
-    /**
-     * Optional. If set, it will contain the target's relevant MIME information.
-     * This includes it's MIME Type, helper app, and whether that helper should be
-     * executed.
-     */
-    readonly attribute nsIMIMEInfo MIMEInfo;
-
-    /**
-     * The id of the download that is stored in the database.
-     */
-    readonly attribute unsigned long id;
-
-    /**
-     * The state of the download.
-     * @see nsIDownloadManager and nsIXPInstallManagerUI
-     */
-    readonly attribute short state;
-};
-
-%{C++
-// {E3FA9D0A-1DD1-11B2-BDEF-8C720B597445}
-#define NS_DOWNLOAD_CID \
-    { 0xe3fa9d0a, 0x1dd1, 0x11b2, { 0xbd, 0xef, 0x8c, 0x72, 0x0b, 0x59, 0x74, 0x45 } }
-%}
--- a/widget/src/windows/nsDragService.cpp
+++ b/widget/src/windows/nsDragService.cpp
@@ -61,17 +61,16 @@
 #include "nsAutoPtr.h"
 
 #include "nsString.h"
 #include "nsEscape.h"
 #include "nsISupportsPrimitives.h"
 #include "nsNetUtil.h"
 #include "nsIURL.h"
 #include "nsCWebBrowserPersist.h"
-#include "nsIDownload.h"
 #include "nsToolkit.h"
 #include "nsCRT.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsUnicharUtils.h"
 
 //-------------------------------------------------------------------------
 //
 // DragService constructor
--- a/xpfe/components/download-manager/public/Makefile.in
+++ b/xpfe/components/download-manager/public/Makefile.in
@@ -41,11 +41,14 @@ srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE    = downloadmanager
 
 XPIDLSRCS = nsIDownloadManager.idl \
             nsIDownloadProgressListener.idl \
+            nsIDownload.idl \
+            nsIProgressDialog.idl \
+            $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/xpfe/components/download-manager/public/nsIDownload.idl
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Blake Ross <blaker@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#include "nsITransfer.idl"
+
+interface nsIURI;
+interface nsILocalFile;
+interface nsIObserver;
+interface nsICancelable;
+interface nsIWebProgressListener;
+interface nsIMIMEInfo;
+
+/**
+ * Represents a download object.
+ *
+ * @note This object is no longer updated once it enters a completed state.
+ *       Completed states are the following:
+ *       nsIDownloadManager::DOWNLOAD_FINISHED
+ *       nsIDownloadManager::DOWNLOAD_FAILED
+ *       nsIDownloadManager::DOWNLOAD_CANCELED
+ */
+[scriptable, uuid(974db2c6-fbd2-4de1-8d24-f54ce4f3e8bc)]
+interface nsIDownload : nsITransfer {
+    
+    /**
+     * The target of a download is always a file on the local file system.
+     */
+    readonly attribute nsILocalFile targetFile;
+
+    /**
+     * The percentage of transfer completed.
+     * If the file size is unknown it'll be -1 here.
+     */
+    readonly attribute PRInt32 percentComplete;
+
+    /**
+     * The amount of bytes downloaded so far.
+     */
+    readonly attribute PRUint64 amountTransferred;
+
+    /**
+     * The size of file in bytes.
+     * Unknown size is represented by LL_MAXUINT.
+     */
+    readonly attribute PRUint64 size;
+    
+    /**
+     * The source of the transfer.
+     */
+    readonly attribute nsIURI source;
+    
+    /**
+     * The target of the transfer.
+     */
+    readonly attribute nsIURI target;
+ 
+    /**
+     * Object that can be used to cancel the download.
+     * Will be null after the download is finished.
+     */
+    readonly attribute nsICancelable cancelable;
+
+    /**
+     * The user-readable description of the transfer.
+     */
+    readonly attribute AString displayName;
+
+    /**
+     * The time a transfer was started.
+     */
+    readonly attribute long long startTime;
+
+    /**
+     * The speed of the transfer in bytes/sec.
+     */
+    readonly attribute double speed;
+
+    /**
+     * Optional. If set, it will contain the target's relevant MIME information.
+     * This includes it's MIME Type, helper app, and whether that helper should be
+     * executed.
+     */
+    readonly attribute nsIMIMEInfo MIMEInfo;
+
+    /**
+     * The id of the download that is stored in the database.
+     */
+    readonly attribute unsigned long id;
+
+    /**
+     * The state of the download.
+     * @see nsIDownloadManager and nsIXPInstallManagerUI
+     */
+    readonly attribute short state;
+};
+
+%{C++
+// {E3FA9D0A-1DD1-11B2-BDEF-8C720B597445}
+#define NS_DOWNLOAD_CID \
+    { 0xe3fa9d0a, 0x1dd1, 0x11b2, { 0xbd, 0xef, 0x8c, 0x72, 0x0b, 0x59, 0x74, 0x45 } }
+%}