Bug 722859 - Support concurrent private/public downloads in the download manager. r=paolo/mak sr=bz ui-r=shorlander
☠☠ backed out by df662e61a310 ☠ ☠
authorJosh Matthews <josh@joshmatthews.net>
Fri, 25 May 2012 12:24:35 +0100
changeset 113483 f077ad362ca32faebd4438917c36dcef9f669e39
parent 113482 6ef7a00233aa539aa230ee1a18af99e7f85151db
child 113484 df662e61a31047e4c296cccfbc82c184418d1e91
push id23872
push useremorley@mozilla.com
push dateFri, 16 Nov 2012 17:06:27 +0000
treeherdermozilla-central@a7ed19f7d21a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaolo, mak, bz, shorlander
bugs722859
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 722859 - Support concurrent private/public downloads in the download manager. r=paolo/mak sr=bz ui-r=shorlander
browser/base/content/sanitize.js
browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
toolkit/components/downloads/nsDownloadManager.cpp
toolkit/components/downloads/nsDownloadManager.h
toolkit/components/downloads/nsIDownload.idl
toolkit/components/downloads/nsIDownloadManager.idl
toolkit/components/downloads/test/unit/test_history_expiration.js
toolkit/components/downloads/test/unit/test_memory_db_support.js
toolkit/components/downloads/test/unit/test_privatebrowsing_cancel.js
toolkit/components/downloads/test/unit/xpcshell.ini
toolkit/forgetaboutsite/ForgetAboutSite.jsm
toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -264,51 +264,54 @@ Sanitizer.prototype = {
     },
     
     downloads: {
       clear: function ()
       {
         var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
                               .getService(Components.interfaces.nsIDownloadManager);
 
-        var dlIDsToRemove = [];
+        var dlsToRemove = [];
         if (this.range) {
           // First, remove the completed/cancelled downloads
           dlMgr.removeDownloadsByTimeframe(this.range[0], this.range[1]);
-          
+
           // Queue up any active downloads that started in the time span as well
-          var dlsEnum = dlMgr.activeDownloads;
-          while(dlsEnum.hasMoreElements()) {
-            var dl = dlsEnum.next();
-            if(dl.startTime >= this.range[0])
-              dlIDsToRemove.push(dl.id);
+          for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) {
+            while (dlsEnum.hasMoreElements()) {
+              var dl = dlsEnum.next();
+              if (dl.startTime >= this.range[0])
+                dlsToRemove.push(dl);
+            }
           }
         }
         else {
           // Clear all completed/cancelled downloads
           dlMgr.cleanUp();
+          dlMgr.cleanUpPrivate();
           
           // Queue up all active ones as well
-          var dlsEnum = dlMgr.activeDownloads;
-          while(dlsEnum.hasMoreElements()) {
-            dlIDsToRemove.push(dlsEnum.next().id);
+          for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) {
+            while (dlsEnum.hasMoreElements()) {
+              dlsToRemove.push(dlsEnum.next());
+            }
           }
         }
-        
+
         // Remove any queued up active downloads
-        dlIDsToRemove.forEach(function(id) {
-          dlMgr.removeDownload(id);
+        dlsToRemove.forEach(function (dl) {
+          dl.remove();
         });
       },
 
       get canClear()
       {
         var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
                               .getService(Components.interfaces.nsIDownloadManager);
-        return dlMgr.canCleanUp;
+        return dlMgr.canCleanUp || dlMgr.canCleanUpPrivate;
       }
     },
     
     passwords: {
       clear: function ()
       {
         var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
                               .getService(Components.interfaces.nsILoginManager);
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -334,16 +334,19 @@ PrivateBrowsingService.prototype = {
     return !cancelEnter.data;
   },
 
   _canLeavePrivateBrowsingMode: function PBS__canLeavePrivateBrowsingMode() {
     let cancelLeave = Cc["@mozilla.org/supports-PRBool;1"].
                       createInstance(Ci.nsISupportsPRBool);
     cancelLeave.data = false;
     this._obs.notifyObservers(cancelLeave, "private-browsing-cancel-vote", "exit");
+    if (!cancelLeave.data) {
+      this._obs.notifyObservers(cancelLeave, "last-pb-context-exiting", null);
+    }
     return !cancelLeave.data;
   },
 
   _getBrowserWindow: function PBS__getBrowserWindow() {
     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
              getService(Ci.nsIWindowMediator);
 
     var win = wm.getMostRecentWindow("navigator:browser");
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -9,23 +9,25 @@
 #include "nsIAlertsService.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIDOMWindow.h"
 #include "nsIDownloadHistory.h"
 #include "nsIDownloadManagerUI.h"
 #include "nsIMIMEService.h"
 #include "nsIParentalControlsService.h"
 #include "nsIPrefService.h"
-#include "nsIPrivateBrowsingService.h"
 #include "nsIPromptService.h"
 #include "nsIResumableChannel.h"
 #include "nsIWebBrowserPersist.h"
 #include "nsIWindowMediator.h"
 #include "nsILocalFileWin.h"
 #include "nsILoadContext.h"
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+#include "nsIPrivateBrowsingService.h"
+#endif
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsArrayEnumerator.h"
 #include "nsCExternalHandlerService.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDownloadManager.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
@@ -73,16 +75,22 @@ using mozilla::downloads::GenerateGUID;
 static const int64_t gUpdateInterval = 400 * PR_USEC_PER_MSEC;
 
 #define DM_SCHEMA_VERSION      9
 #define DM_DB_NAME             NS_LITERAL_STRING("downloads.sqlite")
 #define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt")
 
 #define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
 
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+const bool gUsePerWindowPrivateBrowsing = true;
+#else
+const bool gUsePerWindowPrivateBrowsing = false;
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 //// nsDownloadManager
 
 NS_IMPL_ISUPPORTS4(
   nsDownloadManager
 , nsIDownloadManager
 , nsINavHistoryObserver
 , nsIObserver
@@ -130,32 +138,42 @@ nsDownloadManager::ResumeRetry(nsDownloa
   nsRefPtr<nsDownload> dl = aDl;
 
   // Try to resume the active download
   nsresult rv = dl->Resume();
 
   // If not, try to retry the download
   if (NS_FAILED(rv)) {
     // First cancel the download so it's no longer active
-    rv = CancelDownload(dl->mID);
+    rv = dl->Cancel();
 
     // Then retry it
     if (NS_SUCCEEDED(rv))
-      rv = RetryDownload(dl->mID);
+      rv = dl->Retry();
   }
 
   return rv;
 }
 
 nsresult
 nsDownloadManager::PauseAllDownloads(bool aSetResume)
 {
+  nsresult rv = PauseAllDownloads(mCurrentDownloads, aSetResume);
+  nsresult rv2 = PauseAllDownloads(mCurrentPrivateDownloads, aSetResume);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv2, rv2);
+  return NS_OK;
+}
+
+nsresult
+nsDownloadManager::PauseAllDownloads(nsCOMArray<nsDownload>& aDownloads, bool aSetResume)
+{
   nsresult retVal = NS_OK;
-  for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
-    nsRefPtr<nsDownload> dl = mCurrentDownloads[i];
+  for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
+    nsRefPtr<nsDownload> dl = aDownloads[i];
 
     // Only pause things that need to be paused
     if (!dl->IsPaused()) {
       // Set auto-resume before pausing so that it gets into the DB
       dl->mAutoResume = aSetResume ? nsDownload::AUTO_RESUME :
                                      nsDownload::DONT_RESUME;
 
       // Try to pause the download but don't bail now if we fail
@@ -166,19 +184,29 @@ nsDownloadManager::PauseAllDownloads(boo
   }
 
   return retVal;
 }
 
 nsresult
 nsDownloadManager::ResumeAllDownloads(bool aResumeAll)
 {
+  nsresult rv = ResumeAllDownloads(mCurrentDownloads, aResumeAll);
+  nsresult rv2 = ResumeAllDownloads(mCurrentPrivateDownloads, aResumeAll);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv2, rv2);
+  return NS_OK;
+}
+
+nsresult
+nsDownloadManager::ResumeAllDownloads(nsCOMArray<nsDownload>& aDownloads, bool aResumeAll)
+{
   nsresult retVal = NS_OK;
-  for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
-    nsRefPtr<nsDownload> dl = mCurrentDownloads[i];
+  for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
+    nsRefPtr<nsDownload> dl = aDownloads[i];
 
     // If aResumeAll is true, then resume everything; otherwise, check if the
     // download should auto-resume
     if (aResumeAll || dl->ShouldAutoResume()) {
       // Reset auto-resume before retrying so that it gets into the DB through
       // ResumeRetry's eventual call to SetState. We clear the value now so we
       // don't accidentally query completed downloads that were previously
       // auto-resumed (and try to resume them).
@@ -192,57 +220,67 @@ nsDownloadManager::ResumeAllDownloads(bo
   }
 
   return retVal;
 }
 
 nsresult
 nsDownloadManager::RemoveAllDownloads()
 {
+  nsresult rv = RemoveAllDownloads(mCurrentDownloads);
+  nsresult rv2 = RemoveAllDownloads(mCurrentPrivateDownloads);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv2, rv2);
+  return NS_OK;
+}
+
+nsresult
+nsDownloadManager::RemoveAllDownloads(nsCOMArray<nsDownload>& aDownloads)
+{
   nsresult rv = NS_OK;
-  for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
-    nsRefPtr<nsDownload> dl = mCurrentDownloads[0];
+  for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
+    nsRefPtr<nsDownload> dl = aDownloads[0];
 
     nsresult result = NS_OK;
     if (dl->IsPaused() && GetQuitBehavior() != QUIT_AND_CANCEL)
-      mCurrentDownloads.RemoveObject(dl);
+      aDownloads.RemoveObject(dl);
     else
-      result = CancelDownload(dl->mID);
+      result = dl->Cancel();
 
     // Track the failure, but don't miss out on other downloads
     if (NS_FAILED(result))
       rv = result;
   }
 
   return rv;
 }
 
 nsresult
-nsDownloadManager::RemoveDownloadsForURI(nsIURI *aURI)
+nsDownloadManager::RemoveDownloadsForURI(mozIStorageStatement* aStatement, nsIURI *aURI)
 {
-  mozStorageStatementScoper scope(mGetIdsForURIStatement);
+  mozStorageStatementScoper scope(aStatement);
 
   nsAutoCString source;
   nsresult rv = aURI->GetSpec(source);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mGetIdsForURIStatement->BindUTF8StringByName(
+  rv = aStatement->BindUTF8StringByName(
     NS_LITERAL_CSTRING("source"), source);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasMore = false;
-  nsAutoTArray<int64_t, 4> downloads;
+  nsAutoTArray<nsCString, 4> downloads;
   // Get all the downloads that match the provided URI
-  while (NS_SUCCEEDED(mGetIdsForURIStatement->ExecuteStep(&hasMore)) &&
+  while (NS_SUCCEEDED(aStatement->ExecuteStep(&hasMore)) &&
          hasMore) {
-    int64_t downloadId;
-    rv = mGetIdsForURIStatement->GetInt64(0, &downloadId);
+    nsAutoCString downloadGuid;
+    rv = aStatement->GetUTF8String(0, downloadGuid);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    downloads.AppendElement(downloadId);
+    downloads.AppendElement(downloadGuid);
   }
 
   // Remove each download ignoring any failure so we reach other downloads
   for (int32_t i = downloads.Length(); --i >= 0; )
     (void)RemoveDownload(downloads[i]);
 
   return NS_OK;
 }
@@ -274,64 +312,76 @@ nsDownloadManager::GetFileDBConnection(n
     rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
   }
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return conn.forget();
 }
 
 already_AddRefed<mozIStorageConnection>
-nsDownloadManager::GetMemoryDBConnection() const
+nsDownloadManager::GetPrivateDBConnection() const
 {
   nsCOMPtr<mozIStorageService> storage =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(storage, nullptr);
 
   nsCOMPtr<mozIStorageConnection> conn;
   nsresult rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(conn));
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return conn.forget();
 }
 
 void
-nsDownloadManager::CloseDB()
+nsDownloadManager::CloseAllDBs()
 {
-  DebugOnly<nsresult> rv = mGetIdsForURIStatement->Finalize();
+  CloseDB(mDBConn, mUpdateDownloadStatement, mGetIdsForURIStatement);
+  CloseDB(mPrivateDBConn, mUpdatePrivateDownloadStatement, mGetPrivateIdsForURIStatement);
+}
+
+void
+nsDownloadManager::CloseDB(mozIStorageConnection* aDBConn,
+                           mozIStorageStatement* aUpdateStmt,
+                           mozIStorageStatement* aGetIdsStmt)
+{
+  DebugOnly<nsresult> rv = aGetIdsStmt->Finalize();
   MOZ_ASSERT(NS_SUCCEEDED(rv));
-  rv = mUpdateDownloadStatement->Finalize();
+  rv = aUpdateStmt->Finalize();
   MOZ_ASSERT(NS_SUCCEEDED(rv));
-  rv = mDBConn->AsyncClose(nullptr);
+  rv = aDBConn->AsyncClose(nullptr);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 static nsresult
 InitSQLFunctions(mozIStorageConnection* aDBConn)
 {
   nsresult rv = mozilla::downloads::GenerateGUIDFunction::create(aDBConn);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 nsresult
-nsDownloadManager::InitMemoryDB()
+nsDownloadManager::InitPrivateDB()
 {
   bool ready = false;
-  if (mDBConn && NS_SUCCEEDED(mDBConn->GetConnectionReady(&ready)) && ready)
-    CloseDB();
-  mDBConn = GetMemoryDBConnection();
-  if (!mDBConn)
+  if (mPrivateDBConn && NS_SUCCEEDED(mPrivateDBConn->GetConnectionReady(&ready)) && ready)
+    CloseDB(mPrivateDBConn, mUpdatePrivateDownloadStatement, mGetPrivateIdsForURIStatement);
+  mPrivateDBConn = GetPrivateDBConnection();
+  if (!mPrivateDBConn)
     return NS_ERROR_NOT_AVAILABLE;
 
-  nsresult rv = InitSQLFunctions(mDBConn);
+  nsresult rv = InitSQLFunctions(mPrivateDBConn);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = CreateTable(mPrivateDBConn);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = CreateTable();
+  rv = InitStatements(mPrivateDBConn, getter_AddRefs(mUpdatePrivateDownloadStatement),
+                      getter_AddRefs(mGetPrivateIdsForURIStatement));
   NS_ENSURE_SUCCESS(rv, rv);
-
   return NS_OK;
 }
 
 nsresult
 nsDownloadManager::InitFileDB()
 {
   nsresult rv;
 
@@ -339,29 +389,29 @@ nsDownloadManager::InitFileDB()
   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                               getter_AddRefs(dbFile));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = dbFile->Append(DM_DB_NAME);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool ready = false;
   if (mDBConn && NS_SUCCEEDED(mDBConn->GetConnectionReady(&ready)) && ready)
-    CloseDB();
+    CloseDB(mDBConn, mUpdateDownloadStatement, mGetIdsForURIStatement);
   mDBConn = GetFileDBConnection(dbFile);
   NS_ENSURE_TRUE(mDBConn, NS_ERROR_NOT_AVAILABLE);
 
   rv = InitSQLFunctions(mDBConn);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool tableExists;
   rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_downloads"), &tableExists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!tableExists) {
-    rv = CreateTable();
+    rv = CreateTable(mDBConn);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // We're done with the initialization now and can skip the remaining
     // upgrading logic.
     return NS_OK;
   }
 
   // Checking the database schema now
@@ -621,32 +671,32 @@ nsDownloadManager::InitFileDB()
                                        getter_AddRefs(backup));
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Then we dump it
       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "DROP TABLE moz_downloads"));
       NS_ENSURE_SUCCESS(rv, rv);
 
-      rv = CreateTable();
+      rv = CreateTable(mDBConn);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     break;
   }
 
   return NS_OK;
 }
 
 nsresult
-nsDownloadManager::CreateTable()
+nsDownloadManager::CreateTable(mozIStorageConnection* aDBConn)
 {
-  nsresult rv = mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
+  nsresult rv = aDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
   if (NS_FAILED(rv)) return rv;
 
-  rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+  rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE moz_downloads ("
       "id INTEGER PRIMARY KEY, "
       "name TEXT, "
       "source TEXT, "
       "target TEXT, "
       "tempPath TEXT, "
       "startTime INTEGER, "
       "endTime INTEGER, "
@@ -658,17 +708,17 @@ nsDownloadManager::CreateTable()
       "mimeType TEXT, "
       "preferredApplication TEXT, "
       "preferredAction INTEGER NOT NULL DEFAULT 0, "
       "autoResume INTEGER NOT NULL DEFAULT 0, "
       "guid TEXT"
     ")"));
   if (NS_FAILED(rv)) return rv;
 
-  rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+  rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE UNIQUE INDEX moz_downloads_guid_uniqueindex "
       "ON moz_downloads(guid)"));
   return rv;
 }
 
 nsresult
 nsDownloadManager::RestoreDatabaseState()
 {
@@ -773,20 +823,22 @@ nsDownloadManager::AddDownloadToDB(const
                                    const nsACString &aSource,
                                    const nsACString &aTarget,
                                    const nsAString &aTempPath,
                                    int64_t aStartTime,
                                    int64_t aEndTime,
                                    const nsACString &aMimeType,
                                    const nsACString &aPreferredApp,
                                    nsHandlerInfoAction aPreferredAction,
+                                   bool aPrivate,
                                    nsACString& aNewGUID)
 {
+  mozIStorageConnection* dbConn = aPrivate ? mPrivateDBConn : mDBConn;
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO moz_downloads "
     "(name, source, target, tempPath, startTime, endTime, state, "
      "mimeType, preferredApplication, preferredAction, guid) VALUES "
     "(:name, :source, :target, :tempPath, :startTime, :endTime, :state, "
      ":mimeType, :preferredApplication, :preferredAction, :guid)"),
     getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, 0);
 
@@ -838,48 +890,48 @@ nsDownloadManager::AddDownloadToDB(const
 
   // lock on DB from statement will be released once we return
   return id;
 }
 
 nsresult
 nsDownloadManager::InitDB()
 {
-  nsresult rv = NS_OK;
-
-  switch (mDBType) {
-    case DATABASE_MEMORY:
-      rv = InitMemoryDB();
-      break;
-
-    case DATABASE_DISK:
-      rv = InitFileDB();
-      break;
-
-    default:
-      NS_ERROR("Unexpected value encountered for nsDownloadManager::mDBType");
-      break;
-  }
+  nsresult rv = InitPrivateDB();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = InitFileDB();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = InitStatements(mDBConn, getter_AddRefs(mUpdateDownloadStatement),
+                      getter_AddRefs(mGetIdsForURIStatement));
   NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  return NS_OK;
+}
+
+nsresult
+nsDownloadManager::InitStatements(mozIStorageConnection* aDBConn,
+                                  mozIStorageStatement** aUpdateStatement,
+                                  mozIStorageStatement** aGetIdsStatement)
+{
+  nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "UPDATE moz_downloads "
     "SET tempPath = :tempPath, startTime = :startTime, endTime = :endTime, "
       "state = :state, referrer = :referrer, entityID = :entityID, "
       "currBytes = :currBytes, maxBytes = :maxBytes, autoResume = :autoResume "
-    "WHERE id = :id"), getter_AddRefs(mUpdateDownloadStatement));
+    "WHERE id = :id"), aUpdateStatement);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-    "SELECT id "
+  rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT guid "
     "FROM moz_downloads "
-    "WHERE source = :source"), getter_AddRefs(mGetIdsForURIStatement));
+    "WHERE source = :source"), aGetIdsStatement);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return rv;
+  return NS_OK;
 }
 
 nsresult
 nsDownloadManager::Init()
 {
   // Clean up any old downloads.rdf files from before Firefox 3
   {
     nsCOMPtr<nsIFile> oldDownloadsFile;
@@ -922,24 +974,16 @@ nsDownloadManager::Init()
   // Do things *after* initializing various download manager properties such as
   // restoring downloads to a consistent state
   rv = RestoreDatabaseState();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = RestoreActiveDownloads();
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
 
-  nsCOMPtr<nsIPrivateBrowsingService> pbs =
-    do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
-  if (pbs) {
-    (void)pbs->GetPrivateBrowsingEnabled(&mInPrivateBrowsing);
-    if (mInPrivateBrowsing)
-      OnEnterPrivateBrowsingMode();
-  }
-
   nsCOMPtr<nsINavHistoryService> history =
     do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
 
   (void)mObserverService->NotifyObservers(
                                 static_cast<nsIDownloadManager *>(this),
                                 "download-manager-initialized",
                                 nullptr);
 
@@ -953,18 +997,22 @@ nsDownloadManager::Init()
   (void)mObserverService->AddObserver(this, "offline-requested", true);
   (void)mObserverService->AddObserver(this, "sleep_notification", true);
   (void)mObserverService->AddObserver(this, "wake_notification", true);
   (void)mObserverService->AddObserver(this, "suspend_process_notification", true);
   (void)mObserverService->AddObserver(this, "resume_process_notification", true);
   (void)mObserverService->AddObserver(this, "profile-before-change", true);
   (void)mObserverService->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, true);
   (void)mObserverService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, true);
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
   (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_REQUEST_TOPIC, true);
   (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
+#endif
+  (void)mObserverService->AddObserver(this, "last-pb-context-exited", true);
+  (void)mObserverService->AddObserver(this, "last-pb-context-exiting", true);
 
   if (history)
     (void)history->AddObserver(this, true);
 
   return NS_OK;
 }
 
 int32_t
@@ -1012,72 +1060,107 @@ nsDownloadManager::GetQuitBehavior()
       return QUIT_AND_PAUSE;
     case 2:
       return QUIT_AND_CANCEL;
     default:
       return QUIT_AND_RESUME;
   }
 }
 
+// Using a globally-unique GUID, search all databases (both private and public).
+// A return value of NS_ERROR_NOT_AVAILABLE means no download with the given GUID
+// could be found, either private or public.
+
 nsresult
 nsDownloadManager::GetDownloadFromDB(const nsACString& aGUID, nsDownload **retVal)
 {
   MOZ_ASSERT(!FindDownload(aGUID),
              "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(
+  nsDependentCString query = NS_LITERAL_CSTRING(
     "SELECT id, state, startTime, source, target, tempPath, name, referrer, "
            "entityID, currBytes, maxBytes, mimeType, preferredAction, "
            "preferredApplication, autoResume, guid "
     "FROM moz_downloads "
-    "WHERE guid = :guid"), getter_AddRefs(stmt));
+    "WHERE guid = :guid");
+  // First, let's query the database and see if it even exists
+  nsCOMPtr<mozIStorageStatement> stmt;
+  nsresult rv = mDBConn->CreateStatement(query, getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
   NS_ENSURE_SUCCESS(rv, rv);
-  return GetDownloadFromDB(stmt, retVal);
+
+  rv = GetDownloadFromDB(mDBConn, stmt, retVal);
+
+  // If the download cannot be found in the public database, try again
+  // in the private one. Otherwise, return whatever successful result
+  // or failure obtained from the public database.
+  if (rv == NS_ERROR_NOT_AVAILABLE) {
+    rv = mPrivateDBConn->CreateStatement(query, getter_AddRefs(stmt));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = GetDownloadFromDB(mPrivateDBConn, stmt, retVal);
+
+    // Only if it still cannot be found do we report the failure.
+    if (rv == NS_ERROR_NOT_AVAILABLE) {
+      *retVal = nullptr;
+    }
+  }
+  return rv;
 }
 
 nsresult
-nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
+nsDownloadManager::GetDownloadFromDB(uint32_t aID, nsDownload **retVal)
 {
+  if (gUsePerWindowPrivateBrowsing)
+    NS_WARNING("Using integer IDs without compat mode enabled");
+
   MOZ_ASSERT(!FindDownload(aID),
              "If it is a current download, you should not call this method!");
 
+  nsCOMPtr<mozIStorageConnection> dbConn =
+    IsInGlobalPrivateBrowsing() ? mPrivateDBConn : mDBConn;
+
   // First, let's query the database and see if it even exists
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT id, state, startTime, source, target, tempPath, name, referrer, "
            "entityID, currBytes, maxBytes, mimeType, preferredAction, "
            "preferredApplication, autoResume, guid "
     "FROM moz_downloads "
     "WHERE id = :id"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return GetDownloadFromDB(stmt, retVal);
+  return GetDownloadFromDB(dbConn, stmt, retVal);
 }
 
 nsresult
-nsDownloadManager::GetDownloadFromDB(mozIStorageStatement* stmt, nsDownload **retVal)
+nsDownloadManager::GetDownloadFromDB(mozIStorageConnection* aDBConn,
+                                     mozIStorageStatement* stmt,
+                                     nsDownload **retVal)
 {
   bool hasResults = false;
   nsresult rv = stmt->ExecuteStep(&hasResults);
   if (NS_FAILED(rv) || !hasResults)
     return NS_ERROR_NOT_AVAILABLE;
 
   // We have a download, so lets create it
   nsRefPtr<nsDownload> dl = new nsDownload();
   if (!dl)
     return NS_ERROR_OUT_OF_MEMORY;
-  dl->mPrivate = mInPrivateBrowsing;
+  dl->mPrivate = aDBConn == mPrivateDBConn;
+
+  dl->mDownloadManager = this;
 
   int32_t i = 0;
   // Setting all properties of the download now
   dl->mCancelable = nullptr;
   dl->mID = stmt->AsInt64(i++);
   dl->mDownloadState = stmt->AsInt32(i++);
   dl->mStartTime = stmt->AsInt64(i++);
 
@@ -1189,44 +1272,62 @@ nsDownloadManager::GetDownloadFromDB(moz
   // Addrefing and returning
   NS_ADDREF(*retVal = dl);
   return NS_OK;
 }
 
 nsresult
 nsDownloadManager::AddToCurrentDownloads(nsDownload *aDl)
 {
-  if (!mCurrentDownloads.AppendObject(aDl))
+  nsCOMArray<nsDownload>& currentDownloads =
+    aDl->mPrivate ? mCurrentPrivateDownloads : mCurrentDownloads;
+  if (!currentDownloads.AppendObject(aDl))
     return NS_ERROR_OUT_OF_MEMORY;
 
   aDl->mDownloadManager = this;
   return NS_OK;
 }
 
 void
 nsDownloadManager::SendEvent(nsDownload *aDownload, const char *aTopic)
 {
   (void)mObserverService->NotifyObservers(aDownload, aTopic, nullptr);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIDownloadManager
 
 NS_IMETHODIMP
+nsDownloadManager::GetActivePrivateDownloadCount(int32_t* aResult)
+{
+  *aResult = mCurrentPrivateDownloads.Count();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDownloadManager::GetActiveDownloadCount(int32_t *aResult)
 {
-  *aResult = mCurrentDownloads.Count();
+  *aResult = IsInGlobalPrivateBrowsing() ?
+    mCurrentPrivateDownloads.Count() : mCurrentDownloads.Count();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::GetActiveDownloads(nsISimpleEnumerator **aResult)
 {
-  return NS_NewArrayEnumerator(aResult, mCurrentDownloads);
+  return NS_NewArrayEnumerator(aResult,
+                               IsInGlobalPrivateBrowsing() ?
+                                 mCurrentPrivateDownloads : mCurrentDownloads);
+}
+
+NS_IMETHODIMP
+nsDownloadManager::GetActivePrivateDownloads(nsISimpleEnumerator **aResult)
+{
+  return NS_NewArrayEnumerator(aResult, mCurrentPrivateDownloads);
 }
 
 /**
  * For platforms where helper apps use the downloads directory (i.e. mobile),
  * this should be kept in sync with nsExternalHelperAppService.cpp
  */
 NS_IMETHODIMP
 nsDownloadManager::GetDefaultDownloadsDirectory(nsIFile **aResult)
@@ -1518,17 +1619,17 @@ nsDownloadManager::AddDownload(DownloadT
     }
 
     (void)aMIMEInfo->GetPreferredAction(&action);
   }
 
   int64_t id = AddDownloadToDB(dl->mDisplayName, source, target, tempPath,
                                dl->mStartTime, dl->mLastUpdate,
                                mimeType, persistentDescriptor, action,
-                               dl->mGUID /* outparam */);
+                               dl->mPrivate, dl->mGUID /* outparam */);
   NS_ENSURE_TRUE(id, NS_ERROR_FAILURE);
   dl->mID = id;
 
   rv = AddToCurrentDownloads(dl);
   (void)dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED);
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DOWNLOAD_SCANNER
@@ -1583,16 +1684,20 @@ nsDownloadManager::AddDownload(DownloadT
   NS_ADDREF(*aDownload = dl);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::GetDownload(uint32_t aID, nsIDownload **aDownloadItem)
 {
+  if (gUsePerWindowPrivateBrowsing) {
+    NS_WARNING("Using integer IDs without compat mode enabled");
+  }
+
   nsDownload *itm = FindDownload(aID);
 
   nsRefPtr<nsDownload> dl;
   if (!itm) {
     nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
     NS_ENSURE_SUCCESS(rv, rv);
 
     itm = dl.get();
@@ -1639,22 +1744,39 @@ nsDownloadManager::GetDownloadByGUID(con
     itm = dl.get();
   }
 
   nsRefPtr<AsyncResult> runnable = new AsyncResult(rv, itm, aCallback);
   NS_DispatchToMainThread(runnable);
   return NS_OK;
 }
 
+bool
+nsDownloadManager::IsInGlobalPrivateBrowsing()
+{
+  bool inPrivateBrowsing = false;
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  nsCOMPtr<nsIPrivateBrowsingService> pbs =
+    do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
+  if (pbs) {
+    pbs->GetPrivateBrowsingEnabled(&inPrivateBrowsing);
+  }
+#endif
+  return inPrivateBrowsing;
+}
+
 nsDownload *
 nsDownloadManager::FindDownload(uint32_t aID)
 {
+  nsCOMArray<nsDownload>& currentDownloads =
+    IsInGlobalPrivateBrowsing() ?
+      mCurrentPrivateDownloads : mCurrentDownloads;
   // we shouldn't ever have many downloads, so we can loop over them
-  for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
-    nsDownload *dl = mCurrentDownloads[i];
+  for (int32_t i = currentDownloads.Count() - 1; i >= 0; --i) {
+    nsDownload *dl = currentDownloads[i];
     if (dl->mID == aID)
       return dl;
   }
 
   return nullptr;
 }
 
 nsDownload *
@@ -1662,22 +1784,32 @@ nsDownloadManager::FindDownload(const ns
 {
   // we shouldn't ever have many downloads, so we can loop over them
   for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
     nsDownload *dl = mCurrentDownloads[i];
     if (dl->mGUID == aGUID)
       return dl;
   }
 
+  for (int32-t i = mCurrentPrivateDownloads.Count() - 1; i >= 0; --i) {
+    nsDownload *dl = mCurrentPrivateDownloads[i];
+    if (dl->mGUID == aGUID)
+      return dl;
+  }
+
   return nullptr;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::CancelDownload(uint32_t aID)
 {
+  if (gUsePerWindowPrivateBrowsing) {
+    NS_WARNING("Using integer IDs without compat mode enabled");
+  }
+
   // We AddRef here so we don't lose access to member variables when we remove
   nsRefPtr<nsDownload> dl = FindDownload(aID);
 
   // if it's null, someone passed us a bad id.
   if (!dl)
     return NS_ERROR_FAILURE;
 
   return dl->Cancel();
@@ -1692,16 +1824,20 @@ nsDownloadManager::RetryDownload(const n
 
   return RetryDownload(dl);
 }
 
 
 NS_IMETHODIMP
 nsDownloadManager::RetryDownload(uint32_t aID)
 {
+  if (gUsePerWindowPrivateBrowsing) {
+    NS_WARNING("Using integer IDs without compat mode enabled");
+  }
+
   nsRefPtr<nsDownload> dl;
   nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return RetryDownload(dl);
 }
 
 nsresult
@@ -1751,86 +1887,138 @@ nsDownloadManager::RetryDownload(nsDownl
     dl->mCancelable = nullptr;
     (void)wbp->SetProgressListener(nullptr);
     return rv;
   }
 
   return NS_OK;
 }
 
-nsresult
-nsDownloadManager::RemoveDownload(const nsACString& aGUID)
+static nsresult
+RemoveDownloadByGUID(const nsACString& aGUID, mozIStorageConnection* aDBConn)
 {
-  nsDownload *dl = FindDownload(aGUID);
-  NS_ASSERTION(!dl, "Can't call RemoveDownload on a download in progress!");
-  if (dl)
-    return NS_ERROR_FAILURE;
-
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM moz_downloads "
     "WHERE guid = :guid"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID); // unsigned; 64-bit to prevent overflow
+  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsISupportsPRUint32> id =
-      do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = id->SetData(dl->mID);
+  return NS_OK;
+}
+
+nsresult
+nsDownloadManager::RemoveDownload(const nsACString& aGUID)
+{
+  nsRefPtr<nsDownload> dl = FindDownload(aGUID);
+  MOZ_ASSERT(!dl, "Can't call RemoveDownload on a download in progress!");
+  if (dl)
+    return NS_ERROR_FAILURE;
+
+  nsresult rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Notify the UI with the topic and download id
-  return mObserverService->NotifyObservers(id,
-                                           "download-manager-remove-download",
-                                           nullptr);
+  if (dl->mPrivate) {
+    RemoveDownloadByGUID(aGUID, mPrivateDBConn);
+  } else {
+    RemoveDownloadByGUID(aGUID, mDBConn);
+  }
+
+  return NotifyDownloadRemoval(dl);
 }
 
 NS_IMETHODIMP
 nsDownloadManager::RemoveDownload(uint32_t aID)
 {
-  nsDownload *dl = FindDownload(aID);
-  NS_ASSERTION(!dl, "Can't call RemoveDownload on a download in progress!");
+  if (gUsePerWindowPrivateBrowsing) {
+    NS_WARNING("Using integer IDs without compat mode enabled");
+  }
+
+  nsRefPtr<nsDownload> dl = FindDownload(aID);
+  MOZ_ASSERT(!dl, "Can't call RemoveDownload on a download in progress!");
   if (dl)
     return NS_ERROR_FAILURE;
 
+  nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<mozIStorageConnection> dbConn =
+    IsInGlobalPrivateBrowsing() ? mPrivateDBConn : mDBConn;
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM moz_downloads "
     "WHERE id = :id"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID); // unsigned; 64-bit to prevent overflow
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsISupportsPRUint32> id =
-    do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = id->SetData(aID);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Notify the UI with the topic and download id
-  return mObserverService->NotifyObservers(id,
-                                           "download-manager-remove-download",
-                                           nullptr);
+  return NotifyDownloadRemoval(dl);
 }
 
-NS_IMETHODIMP
-nsDownloadManager::RemoveDownloadsByTimeframe(int64_t aStartTime,
-                                              int64_t aEndTime)
+nsresult
+nsDownloadManager::NotifyDownloadRemoval(nsDownload* aRemoved)
+{
+  nsCOMPtr<nsISupportsPRUint32> id;
+  nsCOMPtr<nsISupportsCString> guid;
+  nsresult rv;
+
+  // Only send an integer ID notification if the download is public,
+  // or we're in global PB compatiblity mode, or we're removing multiple downloads.
+  bool sendDeprecatedNotification = !gUsePerWindowPrivateBrowsing ||
+                                    !(aRemoved && aRemoved->mPrivate);
+
+  if (sendDeprecatedNotification && aRemoved) {
+    id = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    uint32_t dlID;
+    rv = aRemoved->GetId(&dlID);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = id->SetData(dlID);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (sendDeprecatedNotification) {
+    mObserverService->NotifyObservers(id,
+                                     "download-manager-remove-download",
+                                     nullptr);
+  }
+
+  if (aRemoved) {
+    guid = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsAutoCString guidStr;
+    rv = aRemoved->GetGuid(guidStr);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = guid->SetData(guidStr);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  mObserverService->NotifyObservers(guid,
+                                    "download-manager-remove-download-guid",
+                                    nullptr);
+  return NS_OK;
+}
+
+static nsresult
+DoRemoveDownloadsByTimeframe(mozIStorageConnection* aDBConn,
+                             int64_t aStartTime,
+                             int64_t aEndTime)
 {
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM moz_downloads "
     "WHERE startTime >= :startTime "
     "AND startTime <= :endTime "
     "AND state NOT IN (:downloading, :paused, :queued)"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Bind the times
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
@@ -1845,34 +2033,56 @@ nsDownloadManager::RemoveDownloadsByTime
   NS_ENSURE_SUCCESS(rv, rv);
   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Execute
   rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDownloadManager::RemoveDownloadsByTimeframe(int64_t aStartTime,
+                                              int64_t aEndTime)
+{
+  nsresult rv = DoRemoveDownloadsByTimeframe(mDBConn, aStartTime, aEndTime);
+  nsresult rv2 = DoRemoveDownloadsByTimeframe(mPrivateDBConn, aStartTime, aEndTime);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv2, rv2);
+
   // Notify the UI with the topic and null subject to indicate "remove multiple"
-  return mObserverService->NotifyObservers(nullptr,
-                                           "download-manager-remove-download",
-                                           nullptr);
+  return NotifyDownloadRemoval(nullptr);
 }
 
 NS_IMETHODIMP
 nsDownloadManager::CleanUp()
 {
+  return CleanUp(mDBConn);
+}
+
+NS_IMETHODIMP
+nsDownloadManager::CleanUpPrivate()
+{
+  return CleanUp(mPrivateDBConn);
+}
+
+nsresult
+nsDownloadManager::CleanUp(mozIStorageConnection* aDBConn)
+{
   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
                              nsIDownloadManager::DOWNLOAD_FAILED,
                              nsIDownloadManager::DOWNLOAD_CANCELED,
                              nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
                              nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
                              nsIDownloadManager::DOWNLOAD_DIRTY };
 
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM moz_downloads "
     "WHERE state = ? "
       "OR state = ? "
       "OR state = ? "
       "OR state = ? "
       "OR state = ? "
       "OR state = ?"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1880,38 +2090,36 @@ nsDownloadManager::CleanUp()
     rv = stmt->BindInt32ByIndex(i, states[i]);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Notify the UI with the topic and null subject to indicate "remove multiple"
-  return mObserverService->NotifyObservers(nullptr,
-                                           "download-manager-remove-download",
-                                           nullptr);
+  return NotifyDownloadRemoval(nullptr);
 }
 
-NS_IMETHODIMP
-nsDownloadManager::GetCanCleanUp(bool *aResult)
+static nsresult
+DoGetCanCleanUp(mozIStorageConnection* aDBConn, bool *aResult)
 {
   // This method should never return anything but NS_OK for the benefit of
   // unwitting consumers.
   
   *aResult = false;
 
   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
                              nsIDownloadManager::DOWNLOAD_FAILED,
                              nsIDownloadManager::DOWNLOAD_CANCELED,
                              nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
                              nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
                              nsIDownloadManager::DOWNLOAD_DIRTY };
 
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT COUNT(*) "
     "FROM moz_downloads "
     "WHERE state = ? "
       "OR state = ? "
       "OR state = ? "
       "OR state = ? "
       "OR state = ? "
       "OR state = ?"), getter_AddRefs(stmt));
@@ -1931,123 +2139,168 @@ nsDownloadManager::GetCanCleanUp(bool *a
 
   if (count > 0)
     *aResult = true;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDownloadManager::GetCanCleanUp(bool *aResult)
+{
+  return DoGetCanCleanUp(mDBConn, aResult);
+}
+
+NS_IMETHODIMP
+nsDownloadManager::GetCanCleanUpPrivate(bool *aResult)
+{
+  return DoGetCanCleanUp(mPrivateDBConn, aResult);
+}
+
+NS_IMETHODIMP
 nsDownloadManager::PauseDownload(uint32_t aID)
 {
+  if (gUsePerWindowPrivateBrowsing) {
+    NS_WARNING("Using integer IDs without compat mode enabled");
+  }
+
   nsDownload *dl = FindDownload(aID);
   if (!dl)
     return NS_ERROR_FAILURE;
 
   return dl->Pause();
 }
 
 NS_IMETHODIMP
 nsDownloadManager::ResumeDownload(uint32_t aID)
 {
+  if (gUsePerWindowPrivateBrowsing) {
+    NS_WARNING("Using integer IDs without compat mode enabled");
+  }
+
   nsDownload *dl = FindDownload(aID);
   if (!dl)
     return NS_ERROR_FAILURE;
 
   return dl->Resume();
 }
 
 NS_IMETHODIMP
 nsDownloadManager::GetDBConnection(mozIStorageConnection **aDBConn)
 {
-  NS_ADDREF(*aDBConn = mDBConn);
+  NS_ADDREF(*aDBConn = IsInGlobalPrivateBrowsing() ? mPrivateDBConn : mDBConn);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDownloadManager::GetPrivateDBConnection(mozIStorageConnection **aDBConn)
+{
+  NS_ADDREF(*aDBConn = mPrivateDBConn);
+
+  return NS_OK;
+ }
+
+NS_IMETHODIMP
 nsDownloadManager::AddListener(nsIDownloadProgressListener *aListener)
 {
   mListeners.AppendObject(aListener);
-
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDownloadManager::AddPrivacyAwareListener(nsIDownloadProgressListener *aListener)
+{
+  mPrivacyAwareListeners.AppendObject(aListener);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::RemoveListener(nsIDownloadProgressListener *aListener)
 {
   mListeners.RemoveObject(aListener);
-
+  mPrivacyAwareListeners.RemoveObject(aListener);
   return NS_OK;
 }
 
 void
 nsDownloadManager::NotifyListenersOnDownloadStateChange(int16_t aOldState,
-                                                        nsIDownload *aDownload)
+                                                        nsDownload *aDownload)
 {
-  for (int32_t i = mListeners.Count() - 1; i >= 0; --i)
+  for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
+    mPrivacyAwareListeners[i]->OnDownloadStateChange(aOldState, aDownload);
+  }
+
+  // In global PB compatibility mode, it's fine for all listeners to receive
+  // notifications about all downloads. Otherwise, only privacy-aware listeners
+  // should receive notifications about private downloads, while non-privacy-aware
+  // listeners receive no sign they exist.
+  if (aDownload->mPrivate && gUsePerWindowPrivateBrowsing) {
+    return;
+  }
+
+  for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
     mListeners[i]->OnDownloadStateChange(aOldState, aDownload);
+  }
 }
 
 void
 nsDownloadManager::NotifyListenersOnProgressChange(nsIWebProgress *aProgress,
                                                    nsIRequest *aRequest,
                                                    int64_t aCurSelfProgress,
                                                    int64_t aMaxSelfProgress,
                                                    int64_t aCurTotalProgress,
                                                    int64_t aMaxTotalProgress,
-                                                   nsIDownload *aDownload)
+                                                   nsDownload *aDownload)
 {
-  for (int32_t i = mListeners.Count() - 1; i >= 0; --i)
+  for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
+    mPrivacyAwareListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
+                                                aMaxSelfProgress, aCurTotalProgress,
+                                                aMaxTotalProgress, aDownload);
+  }
+
+  // In global PB compatibility mode, it's fine for all listeners to receive
+  // notifications about all downloads. Otherwise, only privacy-aware listeners
+  // should receive notifications about private downloads, while non-privacy-aware
+  // listeners receive no sign they exist.
+  if (aDownload->mPrivate && gUsePerWindowPrivateBrowsing) {
+    return;
+  }
+
+  for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
     mListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
                                     aMaxSelfProgress, aCurTotalProgress,
                                     aMaxTotalProgress, aDownload);
+  }
 }
 
 void
 nsDownloadManager::NotifyListenersOnStateChange(nsIWebProgress *aProgress,
                                                 nsIRequest *aRequest,
                                                 uint32_t aStateFlags,
                                                 nsresult aStatus,
-                                                nsIDownload *aDownload)
+                                                nsDownload *aDownload)
 {
-  for (int32_t i = mListeners.Count() - 1; i >= 0; --i)
+  for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
+    mPrivacyAwareListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus,
+                                             aDownload);
+  }
+
+  // In global PB compatibility mode, it's fine for all listeners to receive
+  // notifications about all downloads. Otherwise, only privacy-aware listeners
+  // should receive notifications about private downloads, while non-privacy-aware
+  // listeners receive no sign they exist.
+  if (aDownload->mPrivate && gUsePerWindowPrivateBrowsing) {
+    return;
+  }
+
+  for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
     mListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus,
                                  aDownload);
-}
-
-nsresult
-nsDownloadManager::SwitchDatabaseTypeTo(enum nsDownloadManager::DatabaseType aType)
-{
-  if (aType == mDBType)
-    return NS_OK; // no-op
-
-  mDBType = aType;
-
-  (void)PauseAllDownloads(true);
-  (void)RemoveAllDownloads();
-
-  nsresult rv = InitDB();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Do things *after* initializing various download manager properties such as
-  // restoring downloads to a consistent state
-  rv = RestoreDatabaseState();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Notify that the database type changed before resuming current downloads
-  (void)mObserverService->NotifyObservers(
-                                static_cast<nsIDownloadManager *>(this),
-                                "download-manager-database-type-changed",
-                                nullptr);
-
-  rv = RestoreActiveDownloads();
-  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
-
-  return rv;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsINavHistoryObserver
 
 NS_IMETHODIMP
 nsDownloadManager::OnBeginUpdateBatch()
 {
@@ -2095,17 +2348,21 @@ nsDownloadManager::OnBeforeDeleteURI(nsI
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::OnDeleteURI(nsIURI *aURI,
                                const nsACString& aGUID,
                                uint16_t aReason)
 {
-  return RemoveDownloadsForURI(aURI);
+  nsresult rv = RemoveDownloadsForURI(mGetIdsForURIStatement, aURI);
+  nsresult rv2 = RemoveDownloadsForURI(mGetPrivateIdsForURIStatement, aURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv2, rv2);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::OnClearHistory()
 {
   return CleanUp();
 }
 
@@ -2130,37 +2387,51 @@ nsDownloadManager::OnDeleteVisits(nsIURI
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIObserver
 
 NS_IMETHODIMP
 nsDownloadManager::Observe(nsISupports *aSubject,
                            const char *aTopic,
                            const PRUnichar *aData)
 {
-  int32_t currDownloadCount = mCurrentDownloads.Count();
+  // If we're in global private browsing mode, the total number of active
+  // downloads we want to warn about is the number of active private downloads.
+  // Otherwise, we need to count the active public downloads that could be lost
+  // by quitting, and add any active private ones as well, since per-window
+  // private browsing may be active.
+  nsCOMArray<nsDownload>& currDownloads =
+    IsInGlobalPrivateBrowsing() ? mCurrentPrivateDownloads : mCurrentDownloads;
+  int32_t currDownloadCount = currDownloads.Count();
 
   // If we don't need to cancel all the downloads on quit, only count the ones
-  // that aren't resumable.
-  if (GetQuitBehavior() != QUIT_AND_CANCEL)
-    for (int32_t i = currDownloadCount - 1; i >= 0; --i)
-      if (mCurrentDownloads[i]->IsResumable())
+  // that aren't resumable (this includes private downloads if we're in
+  // global private browsing mode).
+  if (GetQuitBehavior() != QUIT_AND_CANCEL && !IsInGlobalPrivateBrowsing()) {
+    for (int32_t i = currDownloadCount - 1; i >= 0; --i) {
+      if (currDownloads[i]->IsResumable()) {
         currDownloadCount--;
+      }
+    }
+
+    if (gUsePerWindowPrivateBrowsing) {
+      // We have a count of the public, non-resumable downloads. Now we need
+      // to add the total number of private downloads, since they are in danger
+      // of being lost.
+      currDownloadCount += mCurrentPrivateDownloads.Count();
+    }
+  }
 
   nsresult rv;
   if (strcmp(aTopic, "oncancel") == 0) {
     nsCOMPtr<nsIDownload> dl = do_QueryInterface(aSubject, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    uint32_t id;
-    dl->GetId(&id);
-    nsDownload *dl2 = FindDownload(id);
-    if (dl2)
-      return CancelDownload(id);
+    dl->Cancel();
   } else if (strcmp(aTopic, "profile-before-change") == 0) {
-    CloseDB();
+    CloseAllDBs();
   } else if (strcmp(aTopic, "quit-application") == 0) {
     // Try to pause all downloads and, if appropriate, mark them as auto-resume
     // unless user has specified that downloads should be canceled
     enum QuitBehavior behavior = GetQuitBehavior();
     if (behavior != QUIT_AND_CANCEL)
       (void)PauseAllDownloads(bool(behavior != QUIT_AND_PAUSE));
 
     // Remove downloads to break cycles and cancel downloads
@@ -2202,23 +2473,19 @@ nsDownloadManager::Observe(nsISupports *
     // Pause all downloads, and mark them to auto-resume.
     (void)PauseAllDownloads(true);
   }
   else if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0 &&
            nsDependentString(aData).EqualsLiteral(NS_IOSERVICE_ONLINE)) {
     // We can now resume all downloads that are supposed to auto-resume.
     (void)ResumeAllDownloads(false);
   }
-  else if (strcmp(aTopic, "dlmgr-switchdb") == 0) {
-    if (NS_LITERAL_STRING("memory").Equals(aData))
-      return SwitchDatabaseTypeTo(DATABASE_MEMORY);
-    else if (NS_LITERAL_STRING("disk").Equals(aData))
-      return SwitchDatabaseTypeTo(DATABASE_DISK);
-  }
   else if (strcmp(aTopic, "alertclickcallback") == 0) {
+    //TODO: This doens't make sense when clicking a notification related to
+    //      private downloads when per-window mode is enabled. (bug 810208)
     nsCOMPtr<nsIDownloadManagerUI> dmui =
       do_GetService("@mozilla.org/download-manager-ui;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     return dmui->Show(nullptr, 0, nsIDownloadManagerUI::REASON_USER_INTERACTED);
   } else if (strcmp(aTopic, "sleep_notification") == 0 ||
              strcmp(aTopic, "suspend_process_notification") == 0) {
     // Pause downloads if we're sleeping, and mark the downloads as auto-resume
     (void)PauseAllDownloads(true);
@@ -2231,74 +2498,102 @@ nsDownloadManager::Observe(nsISupports *
 
     // Wait a little bit before trying to resume to avoid resuming when network
     // connections haven't restarted yet
     mResumeOnWakeTimer = do_CreateInstance("@mozilla.org/timer;1");
     if (resumeOnWakeDelay >= 0 && mResumeOnWakeTimer) {
       (void)mResumeOnWakeTimer->InitWithFuncCallback(ResumeOnWakeCallback,
         this, resumeOnWakeDelay, nsITimer::TYPE_ONE_SHOT);
     }
+  } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
+    // Upon leaving private browsing mode, cancel all private downloads,
+    // remove all trace of them, and then blow away the private database
+    // and recreate a blank one.
+    PauseAllDownloads(mCurrentPrivateDownloads, true);
+    RemoveAllDownloads(mCurrentPrivateDownloads);
+    InitPrivateDB();
+  } else if (strcmp(aTopic, "last-pb-context-exiting") == 0) {
+    // If there are active private downloads, prompt the user to confirm leaving
+    // private browsing mode (thereby cancelling them). Otherwise, silently proceed.
+    if (!mCurrentPrivateDownloads.Count())
+      return NS_OK;
+
+    nsCOMPtr<nsISupportsPRBool> cancelDownloads = do_QueryInterface(aSubject, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    ConfirmCancelDownloads(mCurrentPrivateDownloads.Count(), cancelDownloads,
+                           NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertTitle").get(),
+                           NS_LITERAL_STRING("leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple").get(),
+                           NS_LITERAL_STRING("leavePrivateBrowsingWindowsCancelDownloadsAlertMsg").get(),
+                           NS_LITERAL_STRING("dontLeavePrivateBrowsingButton").get());
+#else
+    ConfirmCancelDownloads(mCurrentPrivateDownloads.Count(), cancelDownloads,
+                           NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertTitle").get(),
+                           NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertMsgMultiple").get(),
+                           NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertMsg").get(),
+                           NS_LITERAL_STRING("dontLeavePrivateBrowsingButton").get());
+#endif
   }
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
   else if (strcmp(aTopic, NS_PRIVATE_BROWSING_REQUEST_TOPIC) == 0) {
     if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData) &&
         currDownloadCount) {
       nsCOMPtr<nsISupportsPRBool> cancelDownloads =
         do_QueryInterface(aSubject, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
       ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
                              NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertTitle").get(),
                              NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertMsgMultiple").get(),
                              NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertMsg").get(),
                              NS_LITERAL_STRING("dontEnterPrivateBrowsingButton").get());
     }
-    else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData) &&
-             mCurrentDownloads.Count()) {
-      nsCOMPtr<nsISupportsPRBool> cancelDownloads =
-        do_QueryInterface(aSubject, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
-      ConfirmCancelDownloads(mCurrentDownloads.Count(), cancelDownloads,
-                             NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertTitle").get(),
-                             NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertMsgMultiple").get(),
-                             NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertMsg").get(),
-                             NS_LITERAL_STRING("dontLeavePrivateBrowsingButton").get());
-    }
   }
   else if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
     if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData))
       OnEnterPrivateBrowsingMode();
     else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData))
       OnLeavePrivateBrowsingMode();
   }
+#endif
 
   return NS_OK;
 }
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 void
 nsDownloadManager::OnEnterPrivateBrowsingMode()
 {
   // Pause all downloads, and mark them to auto-resume.
   (void)PauseAllDownloads(true);
-
-  // Switch to using an in-memory DB
-  (void)SwitchDatabaseTypeTo(DATABASE_MEMORY);
-
-  mInPrivateBrowsing = true;
+  (void)RemoveAllDownloads();
+
+  // Notify that the database type changed before resuming current downloads
+  (void)mObserverService->NotifyObservers(
+      static_cast<nsIDownloadManager *>(this),
+      "download-manager-database-type-changed",
+      nullptr);
 }
 
 void
 nsDownloadManager::OnLeavePrivateBrowsingMode()
 {
-  // We can now resume all downloads that are supposed to auto-resume.
-  (void)ResumeAllDownloads(false);
-
-  // Switch back to the on-disk DB again
-  (void)SwitchDatabaseTypeTo(DATABASE_DISK);
-
-  mInPrivateBrowsing = false;
+  nsresult rv = RestoreDatabaseState();
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore database state");
+
+  // Notify that the database type changed before resuming current downloads
+  (void)mObserverService->NotifyObservers(
+      static_cast<nsIDownloadManager *>(this),
+      "download-manager-database-type-changed",
+      nullptr);
+
+  rv = RestoreActiveDownloads();
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
 }
+#endif
 
 void
 nsDownloadManager::ConfirmCancelDownloads(int32_t aCount,
                                           nsISupportsPRBool *aCancelDownloads,
                                           const PRUnichar *aTitle,
                                           const PRUnichar *aCancelMessageMultiple,
                                           const PRUnichar *aCancelMessageSingle,
                                           const PRUnichar *aDontCancelButton)
@@ -2366,18 +2661,18 @@ nsDownload::nsDownload() : mDownloadStat
                            mPercentComplete(0),
                            mCurrBytes(0),
                            mMaxBytes(-1),
                            mStartTime(0),
                            mLastUpdate(PR_Now() - (uint32_t)gUpdateInterval),
                            mResumedAt(-1),
                            mSpeed(0),
                            mHasMultipleFiles(false),
-                           mAutoResume(DONT_RESUME),
-                           mPrivate(false)
+                           mPrivate(false),
+                           mAutoResume(DONT_RESUME)
 {
 }
 
 nsDownload::~nsDownload()
 {
 }
 
 nsresult
@@ -2451,17 +2746,19 @@ nsDownload::SetState(DownloadState aStat
         int32_t alertInterval = 2000;
         if (pref)
           pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
 
         int64_t alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC;
         int64_t goat = PR_Now() - mStartTime;
         showTaskbarAlert = goat > alertIntervalUSec;
 
-        int32_t size = mDownloadManager->mCurrentDownloads.Count();
+        int32_t size = mPrivate ?
+            mDownloadManager->mCurrentPrivateDownloads.Count() :
+            mDownloadManager->mCurrentDownloads.Count();
         if (showTaskbarAlert && size == 0) {
           nsCOMPtr<nsIAlertsService> alerts =
             do_GetService("@mozilla.org/alerts-service;1");
           if (alerts) {
               nsXPIDLString title, message;
 
               mDownloadManager->mBundle->GetStringFromName(
                   NS_LITERAL_STRING("downloadsCompleteTitle").get(),
@@ -2498,18 +2795,17 @@ nsDownload::SetState(DownloadState aStat
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK2)
         // On Windows and Gtk, add the download to the system's "recent documents"
         // list, with a pref to disable.
         {
           bool addToRecentDocs = true;
           if (pref)
             pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
 
-          if (addToRecentDocs &&
-              !nsDownloadManager::gDownloadManagerService->mInPrivateBrowsing) {
+          if (addToRecentDocs && !mPrivate) {
 #ifdef XP_WIN
             ::SHAddToRecentDocs(SHARD_PATHW, path.get());
 #elif defined(MOZ_WIDGET_GTK2)
             GtkRecentManager* manager = gtk_recent_manager_get_default();
 
             gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(),
                                            NULL, NULL);
             if (uri) {
@@ -2570,54 +2866,62 @@ nsDownload::SetState(DownloadState aStat
       nsCOMPtr<nsILocalFileWin> localFileWin(do_QueryInterface(file));
       if (!isTemp && localFileWin)
         (void)localFileWin->SetFileAttributesWin(nsILocalFileWin::WFA_SEARCH_INDEXED);
 #endif
 
 #endif
       // Now remove the download if the user's retention policy is "Remove when Done"
       if (mDownloadManager->GetRetentionBehavior() == 0)
-        mDownloadManager->RemoveDownload(mID);
+        mDownloadManager->RemoveDownload(mGUID);
     }
     break;
   default:
     break;
   }
 
   // Before notifying the listener, we must update the database so that calls
   // to it work out properly.
   nsresult rv = UpdateDB();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDownloadManager->NotifyListenersOnDownloadStateChange(oldState, this);
 
   switch (mDownloadState) {
     case nsIDownloadManager::DOWNLOAD_DOWNLOADING:
       // Only send the dl-start event to downloads that are actually starting.
-      if (oldState == nsIDownloadManager::DOWNLOAD_QUEUED)
-        mDownloadManager->SendEvent(this, "dl-start");
+      if (oldState == nsIDownloadManager::DOWNLOAD_QUEUED) {
+        if (!gUsePerWindowPrivateBrowsing || !mPrivate)
+          mDownloadManager->SendEvent(this, "dl-start");
+      }
       break;
     case nsIDownloadManager::DOWNLOAD_FAILED:
-      mDownloadManager->SendEvent(this, "dl-failed");
+      if (!gUsePerWindowPrivateBrowsing || !mPrivate)
+        mDownloadManager->SendEvent(this, "dl-failed");
       break;
     case nsIDownloadManager::DOWNLOAD_SCANNING:
-      mDownloadManager->SendEvent(this, "dl-scanning");
+      if (!gUsePerWindowPrivateBrowsing || !mPrivate)
+        mDownloadManager->SendEvent(this, "dl-scanning");
       break;
     case nsIDownloadManager::DOWNLOAD_FINISHED:
-      mDownloadManager->SendEvent(this, "dl-done");
+      if (!gUsePerWindowPrivateBrowsing || !mPrivate)
+        mDownloadManager->SendEvent(this, "dl-done");
       break;
     case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
     case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
-      mDownloadManager->SendEvent(this, "dl-blocked");
+      if (!gUsePerWindowPrivateBrowsing || !mPrivate)
+        mDownloadManager->SendEvent(this, "dl-blocked");
       break;
     case nsIDownloadManager::DOWNLOAD_DIRTY:
-      mDownloadManager->SendEvent(this, "dl-dirty");
+      if (!gUsePerWindowPrivateBrowsing || !mPrivate)
+        mDownloadManager->SendEvent(this, "dl-dirty");
       break;
     case nsIDownloadManager::DOWNLOAD_CANCELED:
-      mDownloadManager->SendEvent(this, "dl-cancel");
+      if (!gUsePerWindowPrivateBrowsing || !mPrivate)
+        mDownloadManager->SendEvent(this, "dl-cancel");
       break;
     default:
       break;
   }
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -2950,16 +3254,19 @@ nsDownload::GetSpeed(double *aSpeed)
 {
   *aSpeed = mSpeed;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownload::GetId(uint32_t *aId)
 {
+  if (mPrivate && gUsePerWindowPrivateBrowsing) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
   *aId = mID;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownload::GetGuid(nsACString &aGUID)
 {
   aGUID = mGUID;
@@ -2996,17 +3303,20 @@ nsDownload::Finalize()
   // We're stopping, so break the cycle we created at download start
   mCancelable = nullptr;
 
   // Reset values that aren't needed anymore, so the DB can be updated as well
   mEntityID.Truncate();
   mTempFile = nullptr;
 
   // Remove ourself from the active downloads
-  (void)mDownloadManager->mCurrentDownloads.RemoveObject(this);
+  nsCOMArray<nsDownload>& currentDownloads = mPrivate ?
+    mDownloadManager->mCurrentPrivateDownloads :
+    mDownloadManager->mCurrentDownloads;
+  (void)currentDownloads.RemoveObject(this);
 
   // Make sure we do not automatically resume
   mAutoResume = DONT_RESUME;
 }
 
 nsresult
 nsDownload::ExecuteDesiredAction()
 {
@@ -3238,17 +3548,17 @@ nsDownload::Resume()
   // Create a new channel for the source URI
   nsCOMPtr<nsIChannel> channel;
   nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(wbp));
   rv = NS_NewChannel(getter_AddRefs(channel), mSource, nullptr, nullptr, ir);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
   if (pbChannel) {
-    pbChannel->SetPrivate(mDownloadManager->mInPrivateBrowsing);
+    pbChannel->SetPrivate(mPrivate);
   }
 
   // Make sure we can get a file, either the temporary or the real target, for
   // both purposes of file size and a target to write to
   nsCOMPtr<nsIFile> targetLocalFile(mTempFile);
   if (!targetLocalFile) {
     rv = GetTargetFile(getter_AddRefs(targetLocalFile));
     NS_ENSURE_SUCCESS(rv, rv);
@@ -3352,17 +3662,18 @@ nsDownload::IsFinished()
 }
 
 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;
+  mozIStorageStatement *stmt = mPrivate ?
+    mDownloadManager->mUpdatePrivateDownloadStatement : mDownloadManager->mUpdateDownloadStatement;
 
   nsAutoString tempPath;
   if (mTempFile)
     (void)mTempFile->GetPath(tempPath);
   nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), tempPath);
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), mStartTime);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/toolkit/components/downloads/nsDownloadManager.h
+++ b/toolkit/components/downloads/nsDownloadManager.h
@@ -48,57 +48,53 @@ public:
   NS_DECL_NSINAVHISTORYOBSERVER
   NS_DECL_NSIOBSERVER
 
   nsresult Init();
 
   static nsDownloadManager *GetSingleton();
 
   virtual ~nsDownloadManager();
-  nsDownloadManager() :
-      mDBType(DATABASE_DISK)
-    , mInPrivateBrowsing(false)
+  nsDownloadManager()
 #ifdef DOWNLOAD_SCANNER
-    , mScanner(nullptr)
+    : mScanner(nullptr)
 #endif
   {
   }
 
 protected:
-  enum DatabaseType
-  {
-    DATABASE_DISK = 0, // default
-    DATABASE_MEMORY
-  };
-
   nsresult InitDB();
   nsresult InitFileDB();
-  void CloseDB();
-  nsresult InitMemoryDB();
+  void CloseAllDBs();
+  void CloseDB(mozIStorageConnection* aDBConn,
+               mozIStorageStatement* aUpdateStmt,
+               mozIStorageStatement* aGetIdsStmt);
+  nsresult InitPrivateDB();
   already_AddRefed<mozIStorageConnection> GetFileDBConnection(nsIFile *dbFile) const;
-  already_AddRefed<mozIStorageConnection> GetMemoryDBConnection() const;
-  nsresult SwitchDatabaseTypeTo(enum DatabaseType aType);
-  nsresult CreateTable();
+  already_AddRefed<mozIStorageConnection> GetPrivateDBConnection() const;
+  nsresult CreateTable(mozIStorageConnection* aDBConn);
 
   /**
    * Fix up the database after a crash such as dealing with previously-active
    * downloads. Call this before RestoreActiveDownloads to get the downloads
    * fixed here to be auto-resumed.
    */
   nsresult RestoreDatabaseState();
 
   /**
    * Paused downloads that survive across sessions are considered active, so
    * rebuild the list of these downloads.
    */
   nsresult RestoreActiveDownloads();
 
   nsresult GetDownloadFromDB(const nsACString& aGUID, nsDownload **retVal);
   nsresult GetDownloadFromDB(uint32_t aID, nsDownload **retVal);
-  nsresult GetDownloadFromDB(mozIStorageStatement* stmt, nsDownload **retVal);
+  nsresult GetDownloadFromDB(mozIStorageConnection* aDBConn,
+                             mozIStorageStatement* stmt,
+                             nsDownload **retVal);
 
   /**
    * Specially track the active downloads so that we don't need to check
    * every download to see if they're in progress.
    */
   nsresult AddToCurrentDownloads(nsDownload *aDl);
 
   void SendEvent(nsDownload *aDownload, const char *aTopic);
@@ -112,32 +108,33 @@ protected:
                           const nsACString &aSource,
                           const nsACString &aTarget,
                           const nsAString &aTempPath,
                           int64_t aStartTime,
                           int64_t aEndTime,
                           const nsACString &aMimeType,
                           const nsACString &aPreferredApp,
                           nsHandlerInfoAction aPreferredAction,
+                          bool aPrivate,
                           nsACString &aNewGUID);
 
   void NotifyListenersOnDownloadStateChange(int16_t aOldState,
-                                            nsIDownload *aDownload);
+                                            nsDownload *aDownload);
   void NotifyListenersOnProgressChange(nsIWebProgress *aProgress,
                                        nsIRequest *aRequest,
                                        int64_t aCurSelfProgress,
                                        int64_t aMaxSelfProgress,
                                        int64_t aCurTotalProgress,
                                        int64_t aMaxTotalProgress,
-                                       nsIDownload *aDownload);
+                                       nsDownload *aDownload);
   void NotifyListenersOnStateChange(nsIWebProgress *aProgress,
                                     nsIRequest *aRequest,
                                     uint32_t aStateFlags,
                                     nsresult aStatus,
-                                    nsIDownload *aDownload);
+                                    nsDownload *aDownload);
 
   nsDownload *FindDownload(const nsACString& aGUID);
   nsDownload *FindDownload(uint32_t aID);
 
   /**
    * First try to resume the download, and if that fails, retry it.
    *
    * @param aDl The download to resume and/or retry.
@@ -222,35 +219,50 @@ protected:
   void OnEnterPrivateBrowsingMode();
   void OnLeavePrivateBrowsingMode();
 
   nsresult RetryDownload(const nsACString& aGUID);
   nsresult RetryDownload(nsDownload* dl);
 
   nsresult RemoveDownload(const nsACString& aGUID);
 
+  nsresult NotifyDownloadRemoval(nsDownload* aRemoved);
+
   // Virus scanner for windows
 #ifdef DOWNLOAD_SCANNER
 private:
   nsDownloadScanner* mScanner;
 #endif
 
 private:
+  nsresult CleanUp(mozIStorageConnection* aDBConn);
+  nsresult InitStatements(mozIStorageConnection* aDBConn,
+                          mozIStorageStatement** aUpdateStatement,
+                          mozIStorageStatement** aGetIdsStatement);
+  nsresult RemoveAllDownloads(nsCOMArray<nsDownload>& aDownloads);
+  nsresult PauseAllDownloads(nsCOMArray<nsDownload>& aDownloads, bool aSetResume);
+  nsresult ResumeAllDownloads(nsCOMArray<nsDownload>& aDownloads, bool aResumeAll);
+  nsresult RemoveDownloadsForURI(mozIStorageStatement* aStatement, nsIURI *aURI);
+
+  bool IsInGlobalPrivateBrowsing();
+
   nsCOMArray<nsIDownloadProgressListener> mListeners;
+  nsCOMArray<nsIDownloadProgressListener> mPrivacyAwareListeners;
   nsCOMPtr<nsIStringBundle> mBundle;
   nsCOMPtr<mozIStorageConnection> mDBConn;
+  nsCOMPtr<mozIStorageConnection> mPrivateDBConn;
   nsCOMArray<nsDownload> mCurrentDownloads;
+  nsCOMArray<nsDownload> mCurrentPrivateDownloads;
   nsCOMPtr<nsIObserverService> mObserverService;
   nsCOMPtr<mozIStorageStatement> mUpdateDownloadStatement;
+  nsCOMPtr<mozIStorageStatement> mUpdatePrivateDownloadStatement;
   nsCOMPtr<mozIStorageStatement> mGetIdsForURIStatement;
+  nsCOMPtr<mozIStorageStatement> mGetPrivateIdsForURIStatement;
   nsAutoPtr<mozStorageTransaction> mHistoryTransaction;
 
-  enum DatabaseType mDBType;
-  bool mInPrivateBrowsing;
-
   static nsDownloadManager *gDownloadManagerService;
 
   friend class nsDownload;
 };
 
 class nsDownload : public nsIDownload
 {
 public:
--- a/toolkit/components/downloads/nsIDownload.idl
+++ b/toolkit/components/downloads/nsIDownload.idl
@@ -83,17 +83,23 @@ interface nsIDownload : nsITransfer {
     /**
      * Optional. If set, it will contain the target's relevant MIME information.
      * This includes its 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.
+     * The id of the download that is stored in the database - not globally unique.
+     * For example, a private download and a public one might have identical ids.
+     * Can only be safely used for direct database manipulation in the database that
+     * contains this download. Use the guid property instead for safe, database-agnostic
+     * searching and manipulation.
+     *
+     * @deprecated
      */
     readonly attribute unsigned long id;
 
     /**
      * The guid of the download that is stored in the database.
      * Has the form of twelve alphanumeric characters.
      */
     readonly attribute ACString guid;
--- a/toolkit/components/downloads/nsIDownloadManager.idl
+++ b/toolkit/components/downloads/nsIDownloadManager.idl
@@ -24,17 +24,17 @@ interface nsIDownloadManagerResult : nsI
    * @param aStatus The result code of the operation:
    *        * NS_OK: an item was found. No other success values are returned.
    *        * NS_ERROR_NOT_AVAILABLE: no such item was found.
    *        * Other error values are possible, but less well-defined.
    */
   void handleResult(in nsresult aStatus, in nsIDownload aDownload);
 };
 
-[scriptable, uuid(c2ac096c-53f9-4ef4-85f4-b920a9cc1de5)]
+[scriptable, uuid(b29aac15-7ec4-4ab3-a53b-08f78aed3b34)]
 interface nsIDownloadManager : nsISupports {
   /**
    * Download type for generic file download.
    */
   const short DOWNLOAD_TYPE_DOWNLOAD = 0;
 
   /**
    * Download state for uninitialized download object.
@@ -174,19 +174,24 @@ interface nsIDownloadManager : nsISuppor
    */
   void cancelDownload(in unsigned long aID);
 
   /**
    * Removes the download with the specified id if it's not currently
    * in-progress.  Whereas cancelDownload simply cancels the transfer, but
    * retains information about it, removeDownload removes all knowledge of it.
    *
-   * Also notifies observers of the "download-manager-remove-download" topic
-   * with the download id as the subject to allow any DM consumers to react to
-   * the removal.
+   * Also notifies observers of the "download-manager-remove-download-guid"
+   * topic with the download guid as the subject to allow any DM consumers to
+   * react to the removal.
+   *
+   * Also may notify observers of the "download-manager-remove-download" topic
+   * with the download id as the subject, if the download removed is public
+   * or if global private browsing mode is in use. This notification is deprecated;
+   * the guid notification should be relied upon instead.
    *
    * @param aID The unique ID of the download.
    * @throws NS_ERROR_FAILURE if the download is active.
    */
   void removeDownload(in unsigned long aID);
 
   /**
    * Removes all inactive downloads that were started inclusively within the
@@ -226,47 +231,99 @@ interface nsIDownloadManager : nsISuppor
    *           nsIDownloadManager::DOWNLOAD_FAILED
    */
   void retryDownload(in unsigned long aID);
 
   /**
    * The database connection to the downloads database.
    */
   readonly attribute mozIStorageConnection DBConnection;
+  readonly attribute mozIStorageConnection privateDBConnection;
 
   /** 
    * Whether or not there are downloads that can be cleaned up (removed)
    * i.e. downloads that have completed, have failed or have been canceled.
+   * In global private browsing mode, this reports the status of the relevant
+   * private or public downloads. In per-window mode, it only reports for
+   * public ones.
    */
   readonly attribute boolean canCleanUp;
 
   /** 
+   * Whether or not there are private downloads that can be cleaned up (removed)
+   * i.e. downloads that have completed, have failed or have been canceled.
+   */
+readonly attribute boolean canCleanUpPrivate;
+
+  /** 
    * Removes completed, failed, and canceled downloads from the list.
+   * In global private browsing mode, this operates on the relevant
+   * private or public downloads. In per-window mode, it only operates
+   * on public ones.
    *
-   * Also notifies observers of the "download-manager-remove-download" topic
-   * with a null subject to allow any DM consumers to react to the removals.
+   * Also notifies observers of the "download-manager-remove-download-gui"
+   * and "download-manager-remove-download" topics with a null subject to
+   * allow any DM consumers to react to the removals.
    */
   void cleanUp();
 
   /** 
+   * Removes completed, failed, and canceled downloads from the list
+   * of private downloads.
+   *
+   * Also notifies observers of the "download-manager-remove-download-gui"
+   * and "download-manager-remove-download" topics with a null subject to
+   * allow any DM consumers to react to the removals.
+   */
+void cleanUpPrivate();
+
+  /** 
    * The number of files currently being downloaded.
+   *
+   * In global private browsing mode, this reports the status of the relevant
+   * private or public downloads. In per-window mode, it only reports public
+   * ones.
    */
   readonly attribute long activeDownloadCount;
 
+  /** 
+   * The number of private files currently being downloaded.
+   */
+  readonly attribute long activePrivateDownloadCount;
+
   /**
    * An enumeration of active nsIDownloads
+   *
+   * In global private browsing mode, this reports the status of the relevant
+   * private or public downloads. In per-window mode, it only reports public
+   * ones.
    */
   readonly attribute nsISimpleEnumerator activeDownloads;
 
   /**
-   * Adds a listener from the download manager.
+   * An enumeration of active private nsIDownloads
+   */
+  readonly attribute nsISimpleEnumerator activePrivateDownloads;
+
+  /**
+   * Adds a listener to the download manager. It is expected that this
+   * listener will only access downloads via their deprecated integer id attribute,
+   * and when global private browsing compatibility mode is disabled, this listener
+   * will receive no notifications for downloads marked private.
    */
   void addListener(in nsIDownloadProgressListener aListener);
 
   /**
+   * Adds a listener to the download manager. This listener must be able to
+   * understand and use the guid attribute of downloads for all interactions
+   * with the download manager.
+   */
+  void addPrivacyAwareListener(in nsIDownloadProgressListener aListener);
+
+  /**
    * Removes a listener from the download manager.
    */
   void removeListener(in nsIDownloadProgressListener aListener);
 
   /**
    * Returns the platform default downloads directory.
    */
   readonly attribute nsIFile defaultDownloadsDirectory;
--- a/toolkit/components/downloads/test/unit/test_history_expiration.js
+++ b/toolkit/components/downloads/test/unit/test_history_expiration.js
@@ -84,25 +84,25 @@ function run_test()
 
   // Get the download manager as history observer and batch expirations
   let histobs = dm.QueryInterface(Ci.nsINavHistoryObserver);
   histobs.onBeginUpdateBatch();
 
   // Look for the removed download notification
   let obs = Cc["@mozilla.org/observer-service;1"].
             getService(Ci.nsIObserverService);
-  const kRemoveTopic = "download-manager-remove-download";
+  const kRemoveTopic = "download-manager-remove-download-guid";
   let testObs = {
     observe: function(aSubject, aTopic, aData) {
       if (aTopic != kRemoveTopic)
         return;
 
       // Make sure the removed/expired download was the one we added
-      let id = aSubject.QueryInterface(Ci.nsISupportsPRUint32);
-      do_check_eq(id.data, theId);
+      let id = aSubject.QueryInterface(Ci.nsISupportsCString);
+      do_check_eq(id.data, theGUID);
 
       // We're done!
       histobs.onEndUpdateBatch();
       obs.removeObserver(testObs, kRemoveTopic);
       do_test_finished();
     }
   };
   obs.addObserver(testObs, kRemoveTopic, false);
deleted file mode 100644
--- a/toolkit/components/downloads/test/unit/test_memory_db_support.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// This tests the switching of the download manager database types between disk
-// and memory based databases.  This feature was added in bug 457110.
-
-const nsIDownloadManager = Ci.nsIDownloadManager;
-const dm = Cc["@mozilla.org/download-manager;1"].getService(nsIDownloadManager);
-
-function run_test() {
-  let observer = dm.QueryInterface(Ci.nsIObserver);
-
-  // make sure the initial disk-based DB is initialized correctly
-  let connDisk = dm.DBConnection;
-  do_check_true(connDisk.connectionReady);
-  do_check_neq(connDisk.databaseFile, null);
-
-  // switch to a disk DB -- should be a no-op
-  observer.observe(null, "dlmgr-switchdb", "disk");
-
-  // make sure that the database has not changed
-  do_check_true(dm.DBConnection.connectionReady);
-  do_check_neq(dm.DBConnection.databaseFile, null);
-  do_check_true(connDisk.databaseFile.equals(dm.DBConnection.databaseFile));
-  connDisk = dm.DBConnection;
-  let oldFile = connDisk.databaseFile;
-
-  // switch to a memory DB
-  observer.observe(null, "dlmgr-switchdb", "memory");
-
-  // make sure the DB is has been switched correctly
-  let connMemory = dm.DBConnection;
-  do_check_true(connMemory.connectionReady);
-  do_check_eq(connMemory.databaseFile, null);
-
-  // switch to a memory DB -- should be a no-op
-  observer.observe(null, "dlmgr-switchdb", "memory");
-
-  // make sure that the database is still memory based
-  connMemory = dm.DBConnection;
-  do_check_true(connMemory.connectionReady);
-  do_check_eq(connMemory.databaseFile, null);
-
-  // switch back to the disk DB
-  observer.observe(null, "dlmgr-switchdb", "disk");
-
-  // make sure that the disk database is initialized correctly
-  do_check_true(dm.DBConnection.connectionReady);
-  do_check_neq(dm.DBConnection.databaseFile, null);
-  do_check_true(oldFile.equals(dm.DBConnection.databaseFile));
-}
--- a/toolkit/components/downloads/test/unit/test_privatebrowsing_cancel.js
+++ b/toolkit/components/downloads/test/unit/test_privatebrowsing_cancel.js
@@ -202,16 +202,19 @@ function run_test() {
             // proceed with the transition
             promptService.sayProceed();
 
             // Try to exit the private browsing mode again
             pb.privateBrowsingEnabled = false;
             do_check_true(promptService.wasCalled());
             do_check_false(pb.privateBrowsingEnabled);
 
+            // Simulate leaving PB mode
+            Services.obs.notifyObservers(null, "last-pb-context-exited", null);
+
             // Check that Download-F is canceled and not accessible
             do_check_eq(dlF.state, dm.DOWNLOAD_PAUSED);
 
             finishTest();
           }
           break;
       }
     },
--- a/toolkit/components/downloads/test/unit/xpcshell.ini
+++ b/toolkit/components/downloads/test/unit/xpcshell.ini
@@ -14,17 +14,16 @@ tail =
 # Bug 676989: test hangs consistently on Android
 skip-if = os == "android"
 [test_download_manager.js]
 [test_download_samename.js]
 # Bug 676989: test hangs consistently on Android
 skip-if = os == "android" 
 [test_guid.js]
 [test_history_expiration.js]
-[test_memory_db_support.js]
 [test_offline_support.js]
 [test_old_download_files_removed.js]
 [test_private_resume.js]
 [test_privatebrowsing.js]
 [test_privatebrowsing_cancel.js]
 [test_removeDownloadsByTimeframe.js]
 [test_resume.js]
 [test_sleep_wake.js]
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -96,46 +96,51 @@ this.ForgetAboutSite = {
         }
       }
     }
 
     // Downloads
     let (dm = Cc["@mozilla.org/download-manager;1"].
               getService(Ci.nsIDownloadManager)) {
       // Active downloads
-      let enumerator = dm.activeDownloads;
-      while (enumerator.hasMoreElements()) {
-        let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
-        if (hasRootDomain(dl.source.host, aDomain)) {
-          dm.cancelDownload(dl.id);
-          dm.removeDownload(dl.id);
+      for (let enumerator of [dm.activeDownloads, dm.activePrivateDownloads]) {
+        while (enumerator.hasMoreElements()) {
+          let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
+          if (hasRootDomain(dl.source.host, aDomain)) {
+            dl.cancel();
+            dl.remove();
+          }
+        }
+      }
+
+      function deleteAllLike(db) {
+        // NOTE: This is lossy, but we feel that it is OK to be lossy here and not
+        //       invoke the cost of creating a URI for each download entry and
+        //       ensure that the hostname matches.
+        let stmt = db.createStatement(
+          "DELETE FROM moz_downloads " +
+          "WHERE source LIKE ?1 ESCAPE '/' " +
+          "AND state NOT IN (?2, ?3, ?4)"
+        );
+        let pattern = stmt.escapeStringForLIKE(aDomain, "/");
+        stmt.bindByIndex(0, "%" + pattern + "%");
+        stmt.bindByIndex(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
+        stmt.bindByIndex(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
+        stmt.bindByIndex(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
+        try {
+          stmt.execute();
+        }
+        finally {
+          stmt.finalize();
         }
       }
 
       // Completed downloads
-      let db = dm.DBConnection;
-      // NOTE: This is lossy, but we feel that it is OK to be lossy here and not
-      //       invoke the cost of creating a URI for each download entry and
-      //       ensure that the hostname matches.
-      let stmt = db.createStatement(
-        "DELETE FROM moz_downloads " +
-        "WHERE source LIKE ?1 ESCAPE '/' " +
-        "AND state NOT IN (?2, ?3, ?4)"
-      );
-      let pattern = stmt.escapeStringForLIKE(aDomain, "/");
-      stmt.bindByIndex(0, "%" + pattern + "%");
-      stmt.bindByIndex(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
-      stmt.bindByIndex(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
-      stmt.bindByIndex(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
-      try {
-        stmt.execute();
-      }
-      finally {
-        stmt.finalize();
-      }
+      deleteAllLike(dm.DBConnection);
+      deleteAllLike(dm.privateDBConnection);
 
       // We want to rebuild the list if the UI is showing, so dispatch the
       // observer topic
       let os = Cc["@mozilla.org/observer-service;1"].
                getService(Ci.nsIObserverService);
       os.notifyObservers(null, "download-manager-remove-download", null);
     }
 
--- a/toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/downloads/downloads.properties
@@ -17,30 +17,33 @@ failed=Failed
 finished=Finished
 canceled=Canceled
 
 cannotPause=This download cannot be paused
 
 downloadErrorAlertTitle=Download Error
 downloadErrorGeneric=The download cannot be saved because an unknown error occurred.\n\nPlease try again.
 
+# LOCALIZATION NOTE: we don't have proper plural support in the CPP code; bug 463102
 quitCancelDownloadsAlertTitle=Cancel All Downloads?
 quitCancelDownloadsAlertMsg=If you exit now, 1 download will be canceled. Are you sure you want to exit?
 quitCancelDownloadsAlertMsgMultiple=If you exit now, %S downloads will be canceled. Are you sure you want to exit?
 quitCancelDownloadsAlertMsgMac=If you quit now, 1 download will be canceled. Are you sure you want to quit?
 quitCancelDownloadsAlertMsgMacMultiple=If you quit now, %S downloads will be canceled. Are you sure you want to quit?
 offlineCancelDownloadsAlertTitle=Cancel All Downloads?
 offlineCancelDownloadsAlertMsg=If you go offline now, 1 download will be canceled. Are you sure you want to go offline?
 offlineCancelDownloadsAlertMsgMultiple=If you go offline now, %S downloads will be canceled. Are you sure you want to go offline?
 enterPrivateBrowsingCancelDownloadsAlertTitle=Cancel All Downloads?
 enterPrivateBrowsingCancelDownloadsAlertMsg=If you enter the Private Browsing mode now, 1 download will be canceled. Are you sure you want to enter the Private Browsing mode?
 enterPrivateBrowsingCancelDownloadsAlertMsgMultiple=If you enter the Private Browsing mode now, %S downloads will be canceled. Are you sure you want to enter the Private Browsing mode?
 leavePrivateBrowsingCancelDownloadsAlertTitle=Cancel All Downloads?
 leavePrivateBrowsingCancelDownloadsAlertMsg=If you leave the Private Browsing mode now, 1 download will be canceled. Are you sure you want to leave the Private Browsing mode?
 leavePrivateBrowsingCancelDownloadsAlertMsgMultiple=If you leave the Private Browsing mode now, %S downloads will be canceled. Are you sure you want to leave the Private Browsing mode?
+leavePrivateBrowsingWindowsCancelDownloadsAlertMsg=If you close all Private Browsing windows now, 1 download will be canceled. Are you sure you want to leave the Private Browsing mode?
+leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple=If you close all Private Browsing windows now, %S downloads will be canceled. Are you sure you want to leave the Private Browsing mode?
 cancelDownloadsOKText=Cancel 1 Download
 cancelDownloadsOKTextMultiple=Cancel %S Downloads
 dontQuitButtonWin=Don't Exit
 dontQuitButtonMac=Don't Quit
 dontGoOfflineButton=Stay Online
 dontEnterPrivateBrowsingButton=Don't Enter the Private Browsing Mode
 dontLeavePrivateBrowsingButton=Stay in Private Browsing Mode
 downloadsCompleteTitle=Downloads Complete