toolkit/components/downloads/nsDownloadManager.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/DebugOnly.h"
     8 #include "mozIStorageService.h"
     9 #include "nsIAlertsService.h"
    10 #include "nsIArray.h"
    11 #include "nsIClassInfoImpl.h"
    12 #include "nsIDOMWindow.h"
    13 #include "nsIDownloadHistory.h"
    14 #include "nsIDownloadManagerUI.h"
    15 #include "nsIMIMEService.h"
    16 #include "nsIParentalControlsService.h"
    17 #include "nsIPrefService.h"
    18 #include "nsIPromptService.h"
    19 #include "nsIResumableChannel.h"
    20 #include "nsIWebBrowserPersist.h"
    21 #include "nsIWindowMediator.h"
    22 #include "nsILocalFileWin.h"
    23 #include "nsILoadContext.h"
    24 #include "nsIXULAppInfo.h"
    26 #include "nsAppDirectoryServiceDefs.h"
    27 #include "nsArrayEnumerator.h"
    28 #include "nsCExternalHandlerService.h"
    29 #include "nsDirectoryServiceDefs.h"
    30 #include "nsDownloadManager.h"
    31 #include "nsNetUtil.h"
    32 #include "nsThreadUtils.h"
    34 #include "mozStorageCID.h"
    35 #include "nsDocShellCID.h"
    36 #include "nsEmbedCID.h"
    37 #include "nsToolkitCompsCID.h"
    39 #include "SQLFunctions.h"
    41 #include "mozilla/Preferences.h"
    43 #ifdef XP_WIN
    44 #include <shlobj.h>
    45 #include "nsWindowsHelpers.h"
    46 #ifdef DOWNLOAD_SCANNER
    47 #include "nsDownloadScanner.h"
    48 #endif
    49 #endif
    51 #ifdef XP_MACOSX
    52 #include <CoreFoundation/CoreFoundation.h>
    53 #endif
    55 #ifdef MOZ_WIDGET_ANDROID
    56 #include "AndroidBridge.h"
    57 using namespace mozilla::widget::android;
    58 #endif
    60 #ifdef MOZ_WIDGET_GTK
    61 #include <gtk/gtk.h>
    62 #endif
    64 using namespace mozilla;
    65 using mozilla::downloads::GenerateGUID;
    67 #define DOWNLOAD_MANAGER_BUNDLE "chrome://mozapps/locale/downloads/downloads.properties"
    68 #define DOWNLOAD_MANAGER_ALERT_ICON "chrome://mozapps/skin/downloads/downloadIcon.png"
    69 #define PREF_BD_USEJSTRANSFER "browser.download.useJSTransfer"
    70 #define PREF_BDM_SHOWALERTONCOMPLETE "browser.download.manager.showAlertOnComplete"
    71 #define PREF_BDM_SHOWALERTINTERVAL "browser.download.manager.showAlertInterval"
    72 #define PREF_BDM_RETENTION "browser.download.manager.retention"
    73 #define PREF_BDM_QUITBEHAVIOR "browser.download.manager.quitBehavior"
    74 #define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
    75 #define PREF_BDM_SCANWHENDONE "browser.download.manager.scanWhenDone"
    76 #define PREF_BDM_RESUMEONWAKEDELAY "browser.download.manager.resumeOnWakeDelay"
    77 #define PREF_BH_DELETETEMPFILEONEXIT "browser.helperApps.deleteTempFileOnExit"
    79 static const int64_t gUpdateInterval = 400 * PR_USEC_PER_MSEC;
    81 #define DM_SCHEMA_VERSION      9
    82 #define DM_DB_NAME             NS_LITERAL_STRING("downloads.sqlite")
    83 #define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt")
    85 #define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
    87 ////////////////////////////////////////////////////////////////////////////////
    88 //// nsDownloadManager
    90 NS_IMPL_ISUPPORTS(
    91   nsDownloadManager
    92 , nsIDownloadManager
    93 , nsINavHistoryObserver
    94 , nsIObserver
    95 , nsISupportsWeakReference
    96 )
    98 nsDownloadManager *nsDownloadManager::gDownloadManagerService = nullptr;
   100 nsDownloadManager *
   101 nsDownloadManager::GetSingleton()
   102 {
   103   if (gDownloadManagerService) {
   104     NS_ADDREF(gDownloadManagerService);
   105     return gDownloadManagerService;
   106   }
   108   gDownloadManagerService = new nsDownloadManager();
   109   if (gDownloadManagerService) {
   110 #if defined(MOZ_WIDGET_GTK)
   111     g_type_init();
   112 #endif
   113     NS_ADDREF(gDownloadManagerService);
   114     if (NS_FAILED(gDownloadManagerService->Init()))
   115       NS_RELEASE(gDownloadManagerService);
   116   }
   118   return gDownloadManagerService;
   119 }
   121 nsDownloadManager::~nsDownloadManager()
   122 {
   123 #ifdef DOWNLOAD_SCANNER
   124   if (mScanner) {
   125     delete mScanner;
   126     mScanner = nullptr;
   127   }
   128 #endif
   129   gDownloadManagerService = nullptr;
   130 }
   132 nsresult
   133 nsDownloadManager::ResumeRetry(nsDownload *aDl)
   134 {
   135   // Keep a reference in case we need to cancel the download
   136   nsRefPtr<nsDownload> dl = aDl;
   138   // Try to resume the active download
   139   nsresult rv = dl->Resume();
   141   // If not, try to retry the download
   142   if (NS_FAILED(rv)) {
   143     // First cancel the download so it's no longer active
   144     rv = dl->Cancel();
   146     // Then retry it
   147     if (NS_SUCCEEDED(rv))
   148       rv = dl->Retry();
   149   }
   151   return rv;
   152 }
   154 nsresult
   155 nsDownloadManager::PauseAllDownloads(bool aSetResume)
   156 {
   157   nsresult rv = PauseAllDownloads(mCurrentDownloads, aSetResume);
   158   nsresult rv2 = PauseAllDownloads(mCurrentPrivateDownloads, aSetResume);
   159   NS_ENSURE_SUCCESS(rv, rv);
   160   NS_ENSURE_SUCCESS(rv2, rv2);
   161   return NS_OK;
   162 }
   164 nsresult
   165 nsDownloadManager::PauseAllDownloads(nsCOMArray<nsDownload>& aDownloads, bool aSetResume)
   166 {
   167   nsresult retVal = NS_OK;
   168   for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
   169     nsRefPtr<nsDownload> dl = aDownloads[i];
   171     // Only pause things that need to be paused
   172     if (!dl->IsPaused()) {
   173       // Set auto-resume before pausing so that it gets into the DB
   174       dl->mAutoResume = aSetResume ? nsDownload::AUTO_RESUME :
   175                                      nsDownload::DONT_RESUME;
   177       // Try to pause the download but don't bail now if we fail
   178       nsresult rv = dl->Pause();
   179       if (NS_FAILED(rv))
   180         retVal = rv;
   181     }
   182   }
   184   return retVal;
   185 }
   187 nsresult
   188 nsDownloadManager::ResumeAllDownloads(bool aResumeAll)
   189 {
   190   nsresult rv = ResumeAllDownloads(mCurrentDownloads, aResumeAll);
   191   nsresult rv2 = ResumeAllDownloads(mCurrentPrivateDownloads, aResumeAll);
   192   NS_ENSURE_SUCCESS(rv, rv);
   193   NS_ENSURE_SUCCESS(rv2, rv2);
   194   return NS_OK;
   195 }
   197 nsresult
   198 nsDownloadManager::ResumeAllDownloads(nsCOMArray<nsDownload>& aDownloads, bool aResumeAll)
   199 {
   200   nsresult retVal = NS_OK;
   201   for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
   202     nsRefPtr<nsDownload> dl = aDownloads[i];
   204     // If aResumeAll is true, then resume everything; otherwise, check if the
   205     // download should auto-resume
   206     if (aResumeAll || dl->ShouldAutoResume()) {
   207       // Reset auto-resume before retrying so that it gets into the DB through
   208       // ResumeRetry's eventual call to SetState. We clear the value now so we
   209       // don't accidentally query completed downloads that were previously
   210       // auto-resumed (and try to resume them).
   211       dl->mAutoResume = nsDownload::DONT_RESUME;
   213       // Try to resume/retry the download but don't bail now if we fail
   214       nsresult rv = ResumeRetry(dl);
   215       if (NS_FAILED(rv))
   216         retVal = rv;
   217     }
   218   }
   220   return retVal;
   221 }
   223 nsresult
   224 nsDownloadManager::RemoveAllDownloads()
   225 {
   226   nsresult rv = RemoveAllDownloads(mCurrentDownloads);
   227   nsresult rv2 = RemoveAllDownloads(mCurrentPrivateDownloads);
   228   NS_ENSURE_SUCCESS(rv, rv);
   229   NS_ENSURE_SUCCESS(rv2, rv2);
   230   return NS_OK;
   231 }
   233 nsresult
   234 nsDownloadManager::RemoveAllDownloads(nsCOMArray<nsDownload>& aDownloads)
   235 {
   236   nsresult rv = NS_OK;
   237   for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
   238     nsRefPtr<nsDownload> dl = aDownloads[0];
   240     nsresult result = NS_OK;
   241     if (!dl->mPrivate && dl->IsPaused() && GetQuitBehavior() != QUIT_AND_CANCEL)
   242       aDownloads.RemoveObject(dl);
   243     else
   244       result = dl->Cancel();
   246     // Track the failure, but don't miss out on other downloads
   247     if (NS_FAILED(result))
   248       rv = result;
   249   }
   251   return rv;
   252 }
   254 nsresult
   255 nsDownloadManager::RemoveDownloadsForURI(mozIStorageStatement* aStatement, nsIURI *aURI)
   256 {
   257   mozStorageStatementScoper scope(aStatement);
   259   nsAutoCString source;
   260   nsresult rv = aURI->GetSpec(source);
   261   NS_ENSURE_SUCCESS(rv, rv);
   263   rv = aStatement->BindUTF8StringByName(
   264     NS_LITERAL_CSTRING("source"), source);
   265   NS_ENSURE_SUCCESS(rv, rv);
   267   bool hasMore = false;
   268   nsAutoTArray<nsCString, 4> downloads;
   269   // Get all the downloads that match the provided URI
   270   while (NS_SUCCEEDED(aStatement->ExecuteStep(&hasMore)) &&
   271          hasMore) {
   272     nsAutoCString downloadGuid;
   273     rv = aStatement->GetUTF8String(0, downloadGuid);
   274     NS_ENSURE_SUCCESS(rv, rv);
   276     downloads.AppendElement(downloadGuid);
   277   }
   279   // Remove each download ignoring any failure so we reach other downloads
   280   for (int32_t i = downloads.Length(); --i >= 0; )
   281     (void)RemoveDownload(downloads[i]);
   283   return NS_OK;
   284 }
   286 void // static
   287 nsDownloadManager::ResumeOnWakeCallback(nsITimer *aTimer, void *aClosure)
   288 {
   289   // Resume the downloads that were set to autoResume
   290   nsDownloadManager *dlMgr = static_cast<nsDownloadManager *>(aClosure);
   291   (void)dlMgr->ResumeAllDownloads(false);
   292 }
   294 already_AddRefed<mozIStorageConnection>
   295 nsDownloadManager::GetFileDBConnection(nsIFile *dbFile) const
   296 {
   297   NS_ASSERTION(dbFile, "GetFileDBConnection called with an invalid nsIFile");
   299   nsCOMPtr<mozIStorageService> storage =
   300     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   301   NS_ENSURE_TRUE(storage, nullptr);
   303   nsCOMPtr<mozIStorageConnection> conn;
   304   nsresult rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
   305   if (rv == NS_ERROR_FILE_CORRUPTED) {
   306     // delete and try again, since we don't care so much about losing a user's
   307     // download history
   308     rv = dbFile->Remove(false);
   309     NS_ENSURE_SUCCESS(rv, nullptr);
   310     rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
   311   }
   312   NS_ENSURE_SUCCESS(rv, nullptr);
   314   return conn.forget();
   315 }
   317 already_AddRefed<mozIStorageConnection>
   318 nsDownloadManager::GetPrivateDBConnection() const
   319 {
   320   nsCOMPtr<mozIStorageService> storage =
   321     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   322   NS_ENSURE_TRUE(storage, nullptr);
   324   nsCOMPtr<mozIStorageConnection> conn;
   325   nsresult rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(conn));
   326   NS_ENSURE_SUCCESS(rv, nullptr);
   328   return conn.forget();
   329 }
   331 void
   332 nsDownloadManager::CloseAllDBs()
   333 {
   334   CloseDB(mDBConn, mUpdateDownloadStatement, mGetIdsForURIStatement);
   335   CloseDB(mPrivateDBConn, mUpdatePrivateDownloadStatement, mGetPrivateIdsForURIStatement);
   336 }
   338 void
   339 nsDownloadManager::CloseDB(mozIStorageConnection* aDBConn,
   340                            mozIStorageStatement* aUpdateStmt,
   341                            mozIStorageStatement* aGetIdsStmt)
   342 {
   343   DebugOnly<nsresult> rv = aGetIdsStmt->Finalize();
   344   MOZ_ASSERT(NS_SUCCEEDED(rv));
   345   rv = aUpdateStmt->Finalize();
   346   MOZ_ASSERT(NS_SUCCEEDED(rv));
   347   rv = aDBConn->AsyncClose(nullptr);
   348   MOZ_ASSERT(NS_SUCCEEDED(rv));
   349 }
   351 static nsresult
   352 InitSQLFunctions(mozIStorageConnection* aDBConn)
   353 {
   354   nsresult rv = mozilla::downloads::GenerateGUIDFunction::create(aDBConn);
   355   NS_ENSURE_SUCCESS(rv, rv);
   356   return NS_OK;
   357 }
   359 nsresult
   360 nsDownloadManager::InitPrivateDB()
   361 {
   362   bool ready = false;
   363   if (mPrivateDBConn && NS_SUCCEEDED(mPrivateDBConn->GetConnectionReady(&ready)) && ready)
   364     CloseDB(mPrivateDBConn, mUpdatePrivateDownloadStatement, mGetPrivateIdsForURIStatement);
   365   mPrivateDBConn = GetPrivateDBConnection();
   366   if (!mPrivateDBConn)
   367     return NS_ERROR_NOT_AVAILABLE;
   369   nsresult rv = InitSQLFunctions(mPrivateDBConn);
   370   NS_ENSURE_SUCCESS(rv, rv);
   372   rv = CreateTable(mPrivateDBConn);
   373   NS_ENSURE_SUCCESS(rv, rv);
   375   rv = InitStatements(mPrivateDBConn, getter_AddRefs(mUpdatePrivateDownloadStatement),
   376                       getter_AddRefs(mGetPrivateIdsForURIStatement));
   377   NS_ENSURE_SUCCESS(rv, rv);
   378   return NS_OK;
   379 }
   381 nsresult
   382 nsDownloadManager::InitFileDB()
   383 {
   384   nsresult rv;
   386   nsCOMPtr<nsIFile> dbFile;
   387   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   388                               getter_AddRefs(dbFile));
   389   NS_ENSURE_SUCCESS(rv, rv);
   390   rv = dbFile->Append(DM_DB_NAME);
   391   NS_ENSURE_SUCCESS(rv, rv);
   393   bool ready = false;
   394   if (mDBConn && NS_SUCCEEDED(mDBConn->GetConnectionReady(&ready)) && ready)
   395     CloseDB(mDBConn, mUpdateDownloadStatement, mGetIdsForURIStatement);
   396   mDBConn = GetFileDBConnection(dbFile);
   397   NS_ENSURE_TRUE(mDBConn, NS_ERROR_NOT_AVAILABLE);
   399   rv = InitSQLFunctions(mDBConn);
   400   NS_ENSURE_SUCCESS(rv, rv);
   402   bool tableExists;
   403   rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_downloads"), &tableExists);
   404   NS_ENSURE_SUCCESS(rv, rv);
   406   if (!tableExists) {
   407     rv = CreateTable(mDBConn);
   408     NS_ENSURE_SUCCESS(rv, rv);
   410     // We're done with the initialization now and can skip the remaining
   411     // upgrading logic.
   412     return NS_OK;
   413   }
   415   // Checking the database schema now
   416   int32_t schemaVersion;
   417   rv = mDBConn->GetSchemaVersion(&schemaVersion);
   418   NS_ENSURE_SUCCESS(rv, rv);
   420   // Changing the database?  Be sure to do these two things!
   421   // 1) Increment DM_SCHEMA_VERSION
   422   // 2) Implement the proper downgrade/upgrade code for the current version
   424   switch (schemaVersion) {
   425   // Upgrading
   426   // Every time you increment the database schema, you need to implement
   427   // the upgrading code from the previous version to the new one.
   428   // Also, don't forget to make a unit test to test your upgrading code!
   429   case 1: // Drop a column (iconURL) from the database (bug 385875)
   430     {
   431       // Safely wrap this in a transaction so we don't hose the whole DB
   432       mozStorageTransaction safeTransaction(mDBConn, true);
   434       // Create a temporary table that will store the existing records
   435       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   436         "CREATE TEMPORARY TABLE moz_downloads_backup ("
   437           "id INTEGER PRIMARY KEY, "
   438           "name TEXT, "
   439           "source TEXT, "
   440           "target TEXT, "
   441           "startTime INTEGER, "
   442           "endTime INTEGER, "
   443           "state INTEGER"
   444         ")"));
   445       NS_ENSURE_SUCCESS(rv, rv);
   447       // Insert into a temporary table
   448       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   449         "INSERT INTO moz_downloads_backup "
   450         "SELECT id, name, source, target, startTime, endTime, state "
   451         "FROM moz_downloads"));
   452       NS_ENSURE_SUCCESS(rv, rv);
   454       // Drop the old table
   455       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   456         "DROP TABLE moz_downloads"));
   457       NS_ENSURE_SUCCESS(rv, rv);
   459       // Now recreate it with this schema version
   460       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   461         "CREATE TABLE moz_downloads ("
   462           "id INTEGER PRIMARY KEY, "
   463           "name TEXT, "
   464           "source TEXT, "
   465           "target TEXT, "
   466           "startTime INTEGER, "
   467           "endTime INTEGER, "
   468           "state INTEGER"
   469         ")"));
   470       NS_ENSURE_SUCCESS(rv, rv);
   472       // Insert the data back into it
   473       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   474         "INSERT INTO moz_downloads "
   475         "SELECT id, name, source, target, startTime, endTime, state "
   476         "FROM moz_downloads_backup"));
   477       NS_ENSURE_SUCCESS(rv, rv);
   479       // And drop our temporary table
   480       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   481         "DROP TABLE moz_downloads_backup"));
   482       NS_ENSURE_SUCCESS(rv, rv);
   484       // Finally, update the schemaVersion variable and the database schema
   485       schemaVersion = 2;
   486       rv = mDBConn->SetSchemaVersion(schemaVersion);
   487       NS_ENSURE_SUCCESS(rv, rv);
   488     }
   489     // Fallthrough to the next upgrade
   491   case 2: // Add referrer column to the database
   492     {
   493       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   494         "ALTER TABLE moz_downloads "
   495         "ADD COLUMN referrer TEXT"));
   496       NS_ENSURE_SUCCESS(rv, rv);
   498       // Finally, update the schemaVersion variable and the database schema
   499       schemaVersion = 3;
   500       rv = mDBConn->SetSchemaVersion(schemaVersion);
   501       NS_ENSURE_SUCCESS(rv, rv);
   502     }
   503     // Fallthrough to the next upgrade
   505   case 3: // This version adds a column to the database (entityID)
   506     {
   507       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   508         "ALTER TABLE moz_downloads "
   509         "ADD COLUMN entityID TEXT"));
   510       NS_ENSURE_SUCCESS(rv, rv);
   512       // Finally, update the schemaVersion variable and the database schema
   513       schemaVersion = 4;
   514       rv = mDBConn->SetSchemaVersion(schemaVersion);
   515       NS_ENSURE_SUCCESS(rv, rv);
   516     }
   517     // Fallthrough to the next upgrade
   519   case 4: // This version adds a column to the database (tempPath)
   520     {
   521       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   522         "ALTER TABLE moz_downloads "
   523         "ADD COLUMN tempPath TEXT"));
   524       NS_ENSURE_SUCCESS(rv, rv);
   526       // Finally, update the schemaVersion variable and the database schema
   527       schemaVersion = 5;
   528       rv = mDBConn->SetSchemaVersion(schemaVersion);
   529       NS_ENSURE_SUCCESS(rv, rv);
   530     }
   531     // Fallthrough to the next upgrade
   533   case 5: // This version adds two columns for tracking transfer progress
   534     {
   535       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   536         "ALTER TABLE moz_downloads "
   537         "ADD COLUMN currBytes INTEGER NOT NULL DEFAULT 0"));
   538       NS_ENSURE_SUCCESS(rv, rv);
   540       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   541         "ALTER TABLE moz_downloads "
   542         "ADD COLUMN maxBytes INTEGER NOT NULL DEFAULT -1"));
   543       NS_ENSURE_SUCCESS(rv, rv);
   545       // Finally, update the schemaVersion variable and the database schema
   546       schemaVersion = 6;
   547       rv = mDBConn->SetSchemaVersion(schemaVersion);
   548       NS_ENSURE_SUCCESS(rv, rv);
   549     }
   550     // Fallthrough to the next upgrade
   552   case 6: // This version adds three columns to DB (MIME type related info)
   553     {
   554       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   555         "ALTER TABLE moz_downloads "
   556         "ADD COLUMN mimeType TEXT"));
   557       NS_ENSURE_SUCCESS(rv, rv);
   559       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   560         "ALTER TABLE moz_downloads "
   561         "ADD COLUMN preferredApplication TEXT"));
   562       NS_ENSURE_SUCCESS(rv, rv);
   564       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   565         "ALTER TABLE moz_downloads "
   566         "ADD COLUMN preferredAction INTEGER NOT NULL DEFAULT 0"));
   567       NS_ENSURE_SUCCESS(rv, rv);
   569       // Finally, update the schemaVersion variable and the database schema
   570       schemaVersion = 7;
   571       rv = mDBConn->SetSchemaVersion(schemaVersion);
   572       NS_ENSURE_SUCCESS(rv, rv);
   573     }
   574     // Fallthrough to next upgrade
   576   case 7: // This version adds a column to remember to auto-resume downloads
   577     {
   578       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   579         "ALTER TABLE moz_downloads "
   580         "ADD COLUMN autoResume INTEGER NOT NULL DEFAULT 0"));
   581       NS_ENSURE_SUCCESS(rv, rv);
   583       // Finally, update the schemaVersion variable and the database schema
   584       schemaVersion = 8;
   585       rv = mDBConn->SetSchemaVersion(schemaVersion);
   586       NS_ENSURE_SUCCESS(rv, rv);
   587     }
   588     // Fallthrough to the next upgrade
   590     // Warning: schema versions >=8 must take into account that they can
   591     // be operating on schemas from unknown, future versions that have
   592     // been downgraded. Operations such as adding columns may fail,
   593     // since the column may already exist.
   595   case 8: // This version adds a column for GUIDs
   596     {
   597       bool exists;
   598       rv = mDBConn->IndexExists(NS_LITERAL_CSTRING("moz_downloads_guid_uniqueindex"),
   599                                 &exists);
   600       NS_ENSURE_SUCCESS(rv, rv);
   601       if (!exists) {
   602         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   603           "ALTER TABLE moz_downloads ADD COLUMN guid TEXT"));
   604         NS_ENSURE_SUCCESS(rv, rv);
   605         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   606           "CREATE UNIQUE INDEX moz_downloads_guid_uniqueindex ON moz_downloads (guid)"));
   607         NS_ENSURE_SUCCESS(rv, rv);
   608       }
   610       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   611         "UPDATE moz_downloads SET guid = GENERATE_GUID() WHERE guid ISNULL"));
   612       NS_ENSURE_SUCCESS(rv, rv);
   614       // Finally, update the database schema
   615       schemaVersion = 9;
   616       rv = mDBConn->SetSchemaVersion(schemaVersion);
   617       NS_ENSURE_SUCCESS(rv, rv);
   618     }
   619     // Fallthrough to the next upgrade
   621   // Extra sanity checking for developers
   622 #ifndef DEBUG
   623   case DM_SCHEMA_VERSION:
   624 #endif
   625     break;
   627   case 0:
   628     {
   629       NS_WARNING("Could not get download database's schema version!");
   631       // The table may still be usable - someone may have just messed with the
   632       // schema version, so let's just treat this like a downgrade and verify
   633       // that the needed columns are there.  If they aren't there, we'll drop
   634       // the table anyway.
   635       rv = mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
   636       NS_ENSURE_SUCCESS(rv, rv);
   637     }
   638     // Fallthrough to downgrade check
   640   // Downgrading
   641   // If columns have been added to the table, we can still use the ones we
   642   // understand safely.  If columns have been deleted or alterd, we just
   643   // drop the table and start from scratch.  If you change how a column
   644   // should be interpreted, make sure you also change its name so this
   645   // check will catch it.
   646   default:
   647     {
   648       nsCOMPtr<mozIStorageStatement> stmt;
   649       rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
   650         "SELECT id, name, source, target, tempPath, startTime, endTime, state, "
   651                "referrer, entityID, currBytes, maxBytes, mimeType, "
   652                "preferredApplication, preferredAction, autoResume, guid "
   653         "FROM moz_downloads"), getter_AddRefs(stmt));
   654       if (NS_SUCCEEDED(rv)) {
   655         // We have a database that contains all of the elements that make up
   656         // the latest known schema. Reset the version to force an upgrade
   657         // path if this downgraded database is used in a later version.
   658         mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
   659         break;
   660       }
   662       // if the statement fails, that means all the columns were not there.
   663       // First we backup the database
   664       nsCOMPtr<mozIStorageService> storage =
   665         do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   666       NS_ENSURE_TRUE(storage, NS_ERROR_NOT_AVAILABLE);
   667       nsCOMPtr<nsIFile> backup;
   668       rv = storage->BackupDatabaseFile(dbFile, DM_DB_CORRUPT_FILENAME, nullptr,
   669                                        getter_AddRefs(backup));
   670       NS_ENSURE_SUCCESS(rv, rv);
   672       // Then we dump it
   673       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   674         "DROP TABLE moz_downloads"));
   675       NS_ENSURE_SUCCESS(rv, rv);
   677       rv = CreateTable(mDBConn);
   678       NS_ENSURE_SUCCESS(rv, rv);
   679     }
   680     break;
   681   }
   683   return NS_OK;
   684 }
   686 nsresult
   687 nsDownloadManager::CreateTable(mozIStorageConnection* aDBConn)
   688 {
   689   nsresult rv = aDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
   690   if (NS_FAILED(rv)) return rv;
   692   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   693     "CREATE TABLE moz_downloads ("
   694       "id INTEGER PRIMARY KEY, "
   695       "name TEXT, "
   696       "source TEXT, "
   697       "target TEXT, "
   698       "tempPath TEXT, "
   699       "startTime INTEGER, "
   700       "endTime INTEGER, "
   701       "state INTEGER, "
   702       "referrer TEXT, "
   703       "entityID TEXT, "
   704       "currBytes INTEGER NOT NULL DEFAULT 0, "
   705       "maxBytes INTEGER NOT NULL DEFAULT -1, "
   706       "mimeType TEXT, "
   707       "preferredApplication TEXT, "
   708       "preferredAction INTEGER NOT NULL DEFAULT 0, "
   709       "autoResume INTEGER NOT NULL DEFAULT 0, "
   710       "guid TEXT"
   711     ")"));
   712   if (NS_FAILED(rv)) return rv;
   714   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   715     "CREATE UNIQUE INDEX moz_downloads_guid_uniqueindex "
   716       "ON moz_downloads(guid)"));
   717   return rv;
   718 }
   720 nsresult
   721 nsDownloadManager::RestoreDatabaseState()
   722 {
   723   // Restore downloads that were in a scanning state.  We can assume that they
   724   // have been dealt with by the virus scanner
   725   nsCOMPtr<mozIStorageStatement> stmt;
   726   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
   727     "UPDATE moz_downloads "
   728     "SET state = :state "
   729     "WHERE state = :state_cond"), getter_AddRefs(stmt));
   730   NS_ENSURE_SUCCESS(rv, rv);
   732   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
   733   NS_ENSURE_SUCCESS(rv, rv);
   734   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state_cond"), nsIDownloadManager::DOWNLOAD_SCANNING);
   735   NS_ENSURE_SUCCESS(rv, rv);
   737   rv = stmt->Execute();
   738   NS_ENSURE_SUCCESS(rv, rv);
   740   // Convert supposedly-active downloads into downloads that should auto-resume
   741   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
   742     "UPDATE moz_downloads "
   743     "SET autoResume = :autoResume "
   744     "WHERE state = :notStarted "
   745       "OR state = :queued "
   746       "OR state = :downloading"), getter_AddRefs(stmt));
   747   NS_ENSURE_SUCCESS(rv, rv);
   749   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::AUTO_RESUME);
   750   NS_ENSURE_SUCCESS(rv, rv);
   751   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("notStarted"), nsIDownloadManager::DOWNLOAD_NOTSTARTED);
   752   NS_ENSURE_SUCCESS(rv, rv);
   753   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
   754   NS_ENSURE_SUCCESS(rv, rv);
   755   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING);
   756   NS_ENSURE_SUCCESS(rv, rv);
   758   rv = stmt->Execute();
   759   NS_ENSURE_SUCCESS(rv, rv);
   761   // Switch any download that is supposed to automatically resume and is in a
   762   // finished state to *not* automatically resume.  See Bug 409179 for details.
   763   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
   764     "UPDATE moz_downloads "
   765     "SET autoResume = :autoResume "
   766     "WHERE state = :state "
   767       "AND autoResume = :autoResume_cond"),
   768     getter_AddRefs(stmt));
   769   NS_ENSURE_SUCCESS(rv, rv);
   771   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME);
   772   NS_ENSURE_SUCCESS(rv, rv);
   773   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
   774   NS_ENSURE_SUCCESS(rv, rv);
   775   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume_cond"), nsDownload::AUTO_RESUME);
   776   NS_ENSURE_SUCCESS(rv, rv);
   778   rv = stmt->Execute();
   779   NS_ENSURE_SUCCESS(rv, rv);
   781   return NS_OK;
   782 }
   784 nsresult
   785 nsDownloadManager::RestoreActiveDownloads()
   786 {
   787   nsCOMPtr<mozIStorageStatement> stmt;
   788   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
   789     "SELECT id "
   790     "FROM moz_downloads "
   791     "WHERE (state = :state AND LENGTH(entityID) > 0) "
   792       "OR autoResume != :autoResume"), getter_AddRefs(stmt));
   793   NS_ENSURE_SUCCESS(rv, rv);
   795   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_PAUSED);
   796   NS_ENSURE_SUCCESS(rv, rv);
   797   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME);
   798   NS_ENSURE_SUCCESS(rv, rv);
   800   nsresult retVal = NS_OK;
   801   bool hasResults;
   802   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResults)) && hasResults) {
   803     nsRefPtr<nsDownload> dl;
   804     // Keep trying to add even if we fail one, but make sure to return failure.
   805     // Additionally, be careful to not call anything that tries to change the
   806     // database because we're iterating over a live statement.
   807     if (NS_FAILED(GetDownloadFromDB(stmt->AsInt32(0), getter_AddRefs(dl))) ||
   808         NS_FAILED(AddToCurrentDownloads(dl)))
   809       retVal = NS_ERROR_FAILURE;
   810   }
   812   // Try to resume only the downloads that should auto-resume
   813   rv = ResumeAllDownloads(false);
   814   NS_ENSURE_SUCCESS(rv, rv);
   816   return retVal;
   817 }
   819 int64_t
   820 nsDownloadManager::AddDownloadToDB(const nsAString &aName,
   821                                    const nsACString &aSource,
   822                                    const nsACString &aTarget,
   823                                    const nsAString &aTempPath,
   824                                    int64_t aStartTime,
   825                                    int64_t aEndTime,
   826                                    const nsACString &aMimeType,
   827                                    const nsACString &aPreferredApp,
   828                                    nsHandlerInfoAction aPreferredAction,
   829                                    bool aPrivate,
   830                                    nsACString& aNewGUID)
   831 {
   832   mozIStorageConnection* dbConn = aPrivate ? mPrivateDBConn : mDBConn;
   833   nsCOMPtr<mozIStorageStatement> stmt;
   834   nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
   835     "INSERT INTO moz_downloads "
   836     "(name, source, target, tempPath, startTime, endTime, state, "
   837      "mimeType, preferredApplication, preferredAction, guid) VALUES "
   838     "(:name, :source, :target, :tempPath, :startTime, :endTime, :state, "
   839      ":mimeType, :preferredApplication, :preferredAction, :guid)"),
   840     getter_AddRefs(stmt));
   841   NS_ENSURE_SUCCESS(rv, 0);
   843   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
   844   NS_ENSURE_SUCCESS(rv, 0);
   846   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("source"), aSource);
   847   NS_ENSURE_SUCCESS(rv, 0);
   849   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("target"), aTarget);
   850   NS_ENSURE_SUCCESS(rv, 0);
   852   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), aTempPath);
   853   NS_ENSURE_SUCCESS(rv, 0);
   855   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
   856   NS_ENSURE_SUCCESS(rv, 0);
   858   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime);
   859   NS_ENSURE_SUCCESS(rv, 0);
   861   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_NOTSTARTED);
   862   NS_ENSURE_SUCCESS(rv, 0);
   864   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mimeType"), aMimeType);
   865   NS_ENSURE_SUCCESS(rv, 0);
   867   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("preferredApplication"), aPreferredApp);
   868   NS_ENSURE_SUCCESS(rv, 0);
   870   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("preferredAction"), aPreferredAction);
   871   NS_ENSURE_SUCCESS(rv, 0);
   873   nsAutoCString guid;
   874   rv = GenerateGUID(guid);
   875   NS_ENSURE_SUCCESS(rv, 0);
   876   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
   877   NS_ENSURE_SUCCESS(rv, 0);
   879   bool hasMore;
   880   rv = stmt->ExecuteStep(&hasMore); // we want to keep our lock
   881   NS_ENSURE_SUCCESS(rv, 0);
   883   int64_t id = 0;
   884   rv = dbConn->GetLastInsertRowID(&id);
   885   NS_ENSURE_SUCCESS(rv, 0);
   887   aNewGUID = guid;
   889   // lock on DB from statement will be released once we return
   890   return id;
   891 }
   893 nsresult
   894 nsDownloadManager::InitDB()
   895 {
   896   nsresult rv = InitPrivateDB();
   897   NS_ENSURE_SUCCESS(rv, rv);
   899   rv = InitFileDB();
   900   NS_ENSURE_SUCCESS(rv, rv);
   902   rv = InitStatements(mDBConn, getter_AddRefs(mUpdateDownloadStatement),
   903                       getter_AddRefs(mGetIdsForURIStatement));
   904   NS_ENSURE_SUCCESS(rv, rv);
   905   return NS_OK;
   906 }
   908 nsresult
   909 nsDownloadManager::InitStatements(mozIStorageConnection* aDBConn,
   910                                   mozIStorageStatement** aUpdateStatement,
   911                                   mozIStorageStatement** aGetIdsStatement)
   912 {
   913   nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
   914     "UPDATE moz_downloads "
   915     "SET tempPath = :tempPath, startTime = :startTime, endTime = :endTime, "
   916       "state = :state, referrer = :referrer, entityID = :entityID, "
   917       "currBytes = :currBytes, maxBytes = :maxBytes, autoResume = :autoResume "
   918     "WHERE id = :id"), aUpdateStatement);
   919   NS_ENSURE_SUCCESS(rv, rv);
   921   rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
   922     "SELECT guid "
   923     "FROM moz_downloads "
   924     "WHERE source = :source"), aGetIdsStatement);
   925   NS_ENSURE_SUCCESS(rv, rv);
   927   return NS_OK;
   928 }
   930 nsresult
   931 nsDownloadManager::Init()
   932 {
   933   nsresult rv;
   935   nsCOMPtr<nsIStringBundleService> bundleService =
   936     mozilla::services::GetStringBundleService();
   937   if (!bundleService)
   938     return NS_ERROR_FAILURE;
   940   rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE,
   941                                    getter_AddRefs(mBundle));
   942   NS_ENSURE_SUCCESS(rv, rv);
   944 #if !defined(MOZ_JSDOWNLOADS)
   945   // When MOZ_JSDOWNLOADS is undefined, we still check the preference that can
   946   // be used to enable the JavaScript API during the migration process.
   947   mUseJSTransfer = Preferences::GetBool(PREF_BD_USEJSTRANSFER, false);
   948 #else
   950   nsAutoCString appID;
   951   nsCOMPtr<nsIXULAppInfo> info = do_GetService("@mozilla.org/xre/app-info;1");
   952   if (info) {
   953     rv = info->GetID(appID);
   954     NS_ENSURE_SUCCESS(rv, rv);
   955   }
   957   // The webapp runtime doesn't use the new JS downloads API yet.
   958   // The conversion of the webapp runtime to use the JavaScript API for
   959   // downloads is tracked in bug 911636.
   960   if (appID.EqualsLiteral("webapprt@mozilla.org")) {
   961     mUseJSTransfer = false;
   962   } else {
   963 #if !defined(XP_WIN)
   964     mUseJSTransfer = true;
   965 #else
   966     // When MOZ_JSDOWNLOADS is defined on Windows, this component is disabled
   967     // unless we are running in Windows Metro.  The conversion of Windows Metro
   968     // to use the JavaScript API for downloads is tracked in bug 906042.
   969     mUseJSTransfer = !IsRunningInWindowsMetro();
   970 #endif
   971   }
   973 #endif
   975   if (mUseJSTransfer)
   976     return NS_OK;
   978   // Clean up any old downloads.rdf files from before Firefox 3
   979   {
   980     nsCOMPtr<nsIFile> oldDownloadsFile;
   981     bool fileExists;
   982     if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE,
   983           getter_AddRefs(oldDownloadsFile))) &&
   984         NS_SUCCEEDED(oldDownloadsFile->Exists(&fileExists)) &&
   985         fileExists) {
   986       (void)oldDownloadsFile->Remove(false);
   987     }
   988   }
   990   mObserverService = mozilla::services::GetObserverService();
   991   if (!mObserverService)
   992     return NS_ERROR_FAILURE;
   994   rv = InitDB();
   995   NS_ENSURE_SUCCESS(rv, rv);
   997 #ifdef DOWNLOAD_SCANNER
   998   mScanner = new nsDownloadScanner();
   999   if (!mScanner)
  1000     return NS_ERROR_OUT_OF_MEMORY;
  1001   rv = mScanner->Init();
  1002   if (NS_FAILED(rv)) {
  1003     delete mScanner;
  1004     mScanner = nullptr;
  1006 #endif
  1008   // Do things *after* initializing various download manager properties such as
  1009   // restoring downloads to a consistent state
  1010   rv = RestoreDatabaseState();
  1011   NS_ENSURE_SUCCESS(rv, rv);
  1013   rv = RestoreActiveDownloads();
  1014   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
  1016   nsCOMPtr<nsINavHistoryService> history =
  1017     do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
  1019   (void)mObserverService->NotifyObservers(
  1020                                 static_cast<nsIDownloadManager *>(this),
  1021                                 "download-manager-initialized",
  1022                                 nullptr);
  1024   // The following AddObserver calls must be the last lines in this function,
  1025   // because otherwise, this function may fail (and thus, this object would be not
  1026   // completely initialized), but the observerservice would still keep a reference
  1027   // to us and notify us about shutdown, which may cause crashes.
  1028   // failure to add an observer is not critical
  1029   (void)mObserverService->AddObserver(this, "quit-application", true);
  1030   (void)mObserverService->AddObserver(this, "quit-application-requested", true);
  1031   (void)mObserverService->AddObserver(this, "offline-requested", true);
  1032   (void)mObserverService->AddObserver(this, "sleep_notification", true);
  1033   (void)mObserverService->AddObserver(this, "wake_notification", true);
  1034   (void)mObserverService->AddObserver(this, "suspend_process_notification", true);
  1035   (void)mObserverService->AddObserver(this, "resume_process_notification", true);
  1036   (void)mObserverService->AddObserver(this, "profile-before-change", true);
  1037   (void)mObserverService->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, true);
  1038   (void)mObserverService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, true);
  1039   (void)mObserverService->AddObserver(this, "last-pb-context-exited", true);
  1040   (void)mObserverService->AddObserver(this, "last-pb-context-exiting", true);
  1042   if (history)
  1043     (void)history->AddObserver(this, true);
  1045   return NS_OK;
  1048 int32_t
  1049 nsDownloadManager::GetRetentionBehavior()
  1051   // We use 0 as the default, which is "remove when done"
  1052   nsresult rv;
  1053   nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  1054   NS_ENSURE_SUCCESS(rv, 0);
  1056   int32_t val;
  1057   rv = pref->GetIntPref(PREF_BDM_RETENTION, &val);
  1058   NS_ENSURE_SUCCESS(rv, 0);
  1060   // Allow the Downloads Panel to change the retention behavior.  We do this to
  1061   // allow proper migration to the new feature when using the same profile on
  1062   // multiple versions of the product (bug 697678).  Implementation note: in
  1063   // order to allow observers to change the retention value, we have to pass an
  1064   // object in the aSubject parameter, we cannot use aData for that.
  1065   nsCOMPtr<nsISupportsPRInt32> retentionBehavior =
  1066     do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID);
  1067   retentionBehavior->SetData(val);
  1068   (void)mObserverService->NotifyObservers(retentionBehavior,
  1069                                           "download-manager-change-retention",
  1070                                           nullptr);
  1071   retentionBehavior->GetData(&val);
  1073   return val;
  1076 enum nsDownloadManager::QuitBehavior
  1077 nsDownloadManager::GetQuitBehavior()
  1079   // We use 0 as the default, which is "remember and resume the download"
  1080   nsresult rv;
  1081   nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  1082   NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME);
  1084   int32_t val;
  1085   rv = pref->GetIntPref(PREF_BDM_QUITBEHAVIOR, &val);
  1086   NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME);
  1088   switch (val) {
  1089     case 1:
  1090       return QUIT_AND_PAUSE;
  1091     case 2:
  1092       return QUIT_AND_CANCEL;
  1093     default:
  1094       return QUIT_AND_RESUME;
  1098 // Using a globally-unique GUID, search all databases (both private and public).
  1099 // A return value of NS_ERROR_NOT_AVAILABLE means no download with the given GUID
  1100 // could be found, either private or public.
  1102 nsresult
  1103 nsDownloadManager::GetDownloadFromDB(const nsACString& aGUID, nsDownload **retVal)
  1105   MOZ_ASSERT(!FindDownload(aGUID),
  1106              "If it is a current download, you should not call this method!");
  1108   NS_NAMED_LITERAL_CSTRING(query,
  1109     "SELECT id, state, startTime, source, target, tempPath, name, referrer, "
  1110            "entityID, currBytes, maxBytes, mimeType, preferredAction, "
  1111            "preferredApplication, autoResume, guid "
  1112     "FROM moz_downloads "
  1113     "WHERE guid = :guid");
  1114   // First, let's query the database and see if it even exists
  1115   nsCOMPtr<mozIStorageStatement> stmt;
  1116   nsresult rv = mDBConn->CreateStatement(query, getter_AddRefs(stmt));
  1117   NS_ENSURE_SUCCESS(rv, rv);
  1119   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
  1120   NS_ENSURE_SUCCESS(rv, rv);
  1122   rv = GetDownloadFromDB(mDBConn, stmt, retVal);
  1124   // If the download cannot be found in the public database, try again
  1125   // in the private one. Otherwise, return whatever successful result
  1126   // or failure obtained from the public database.
  1127   if (rv == NS_ERROR_NOT_AVAILABLE) {
  1128     rv = mPrivateDBConn->CreateStatement(query, getter_AddRefs(stmt));
  1129     NS_ENSURE_SUCCESS(rv, rv);
  1131     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
  1132     NS_ENSURE_SUCCESS(rv, rv);
  1134     rv = GetDownloadFromDB(mPrivateDBConn, stmt, retVal);
  1136     // Only if it still cannot be found do we report the failure.
  1137     if (rv == NS_ERROR_NOT_AVAILABLE) {
  1138       *retVal = nullptr;
  1141   return rv;
  1144 nsresult
  1145 nsDownloadManager::GetDownloadFromDB(uint32_t aID, nsDownload **retVal)
  1147   NS_WARNING("Using integer IDs without compat mode enabled");
  1149   MOZ_ASSERT(!FindDownload(aID),
  1150              "If it is a current download, you should not call this method!");
  1152   // First, let's query the database and see if it even exists
  1153   nsCOMPtr<mozIStorageStatement> stmt;
  1154   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
  1155     "SELECT id, state, startTime, source, target, tempPath, name, referrer, "
  1156            "entityID, currBytes, maxBytes, mimeType, preferredAction, "
  1157            "preferredApplication, autoResume, guid "
  1158     "FROM moz_downloads "
  1159     "WHERE id = :id"), getter_AddRefs(stmt));
  1160   NS_ENSURE_SUCCESS(rv, rv);
  1162   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID);
  1163   NS_ENSURE_SUCCESS(rv, rv);
  1165   return GetDownloadFromDB(mDBConn, stmt, retVal);
  1168 nsresult
  1169 nsDownloadManager::GetDownloadFromDB(mozIStorageConnection* aDBConn,
  1170                                      mozIStorageStatement* stmt,
  1171                                      nsDownload **retVal)
  1173   bool hasResults = false;
  1174   nsresult rv = stmt->ExecuteStep(&hasResults);
  1175   if (NS_FAILED(rv) || !hasResults)
  1176     return NS_ERROR_NOT_AVAILABLE;
  1178   // We have a download, so lets create it
  1179   nsRefPtr<nsDownload> dl = new nsDownload();
  1180   if (!dl)
  1181     return NS_ERROR_OUT_OF_MEMORY;
  1182   dl->mPrivate = aDBConn == mPrivateDBConn;
  1184   dl->mDownloadManager = this;
  1186   int32_t i = 0;
  1187   // Setting all properties of the download now
  1188   dl->mCancelable = nullptr;
  1189   dl->mID = stmt->AsInt64(i++);
  1190   dl->mDownloadState = stmt->AsInt32(i++);
  1191   dl->mStartTime = stmt->AsInt64(i++);
  1193   nsCString source;
  1194   stmt->GetUTF8String(i++, source);
  1195   rv = NS_NewURI(getter_AddRefs(dl->mSource), source);
  1196   NS_ENSURE_SUCCESS(rv, rv);
  1198   nsCString target;
  1199   stmt->GetUTF8String(i++, target);
  1200   rv = NS_NewURI(getter_AddRefs(dl->mTarget), target);
  1201   NS_ENSURE_SUCCESS(rv, rv);
  1203   nsString tempPath;
  1204   stmt->GetString(i++, tempPath);
  1205   if (!tempPath.IsEmpty()) {
  1206     rv = NS_NewLocalFile(tempPath, true, getter_AddRefs(dl->mTempFile));
  1207     NS_ENSURE_SUCCESS(rv, rv);
  1210   stmt->GetString(i++, dl->mDisplayName);
  1212   nsCString referrer;
  1213   rv = stmt->GetUTF8String(i++, referrer);
  1214   if (NS_SUCCEEDED(rv) && !referrer.IsEmpty()) {
  1215     rv = NS_NewURI(getter_AddRefs(dl->mReferrer), referrer);
  1216     NS_ENSURE_SUCCESS(rv, rv);
  1219   rv = stmt->GetUTF8String(i++, dl->mEntityID);
  1220   NS_ENSURE_SUCCESS(rv, rv);
  1222   int64_t currBytes = stmt->AsInt64(i++);
  1223   int64_t maxBytes = stmt->AsInt64(i++);
  1224   dl->SetProgressBytes(currBytes, maxBytes);
  1226   // Build mMIMEInfo only if the mimeType in DB is not empty
  1227   nsAutoCString mimeType;
  1228   rv = stmt->GetUTF8String(i++, mimeType);
  1229   NS_ENSURE_SUCCESS(rv, rv);
  1231   if (!mimeType.IsEmpty()) {
  1232     nsCOMPtr<nsIMIMEService> mimeService =
  1233       do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
  1234     NS_ENSURE_SUCCESS(rv, rv);
  1236     rv = mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
  1237                                               getter_AddRefs(dl->mMIMEInfo));
  1238     NS_ENSURE_SUCCESS(rv, rv);
  1240     nsHandlerInfoAction action = stmt->AsInt32(i++);
  1241     rv = dl->mMIMEInfo->SetPreferredAction(action);
  1242     NS_ENSURE_SUCCESS(rv, rv);
  1244     nsAutoCString persistentDescriptor;
  1245     rv = stmt->GetUTF8String(i++, persistentDescriptor);
  1246     NS_ENSURE_SUCCESS(rv, rv);
  1248     if (!persistentDescriptor.IsEmpty()) {
  1249       nsCOMPtr<nsILocalHandlerApp> handler =
  1250         do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
  1251       NS_ENSURE_SUCCESS(rv, rv);
  1253       nsCOMPtr<nsIFile> localExecutable;
  1254       rv = NS_NewNativeLocalFile(EmptyCString(), false,
  1255                                  getter_AddRefs(localExecutable));
  1256       NS_ENSURE_SUCCESS(rv, rv);
  1258       rv = localExecutable->SetPersistentDescriptor(persistentDescriptor);
  1259       NS_ENSURE_SUCCESS(rv, rv);
  1261       rv = handler->SetExecutable(localExecutable);
  1262       NS_ENSURE_SUCCESS(rv, rv);
  1264       rv = dl->mMIMEInfo->SetPreferredApplicationHandler(handler);
  1265       NS_ENSURE_SUCCESS(rv, rv);
  1267   } else {
  1268     // Compensate for the i++s skipped in the true block
  1269     i += 2;
  1272   dl->mAutoResume =
  1273     static_cast<enum nsDownload::AutoResume>(stmt->AsInt32(i++));
  1275   rv = stmt->GetUTF8String(i++, dl->mGUID);
  1276   NS_ENSURE_SUCCESS(rv, rv);
  1278   // Handle situations where we load a download from a database that has been
  1279   // used in an older version and not gone through the upgrade path (ie. it
  1280   // contains empty GUID entries).
  1281   if (dl->mGUID.IsEmpty()) {
  1282     rv = GenerateGUID(dl->mGUID);
  1283     NS_ENSURE_SUCCESS(rv, rv);
  1284     nsCOMPtr<mozIStorageStatement> stmt;
  1285     rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
  1286                                     "UPDATE moz_downloads SET guid = :guid "
  1287                                     "WHERE id = :id"),
  1288                                   getter_AddRefs(stmt));
  1289     NS_ENSURE_SUCCESS(rv, rv);
  1290     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), dl->mGUID);
  1291     NS_ENSURE_SUCCESS(rv, rv);
  1292     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), dl->mID);
  1293     NS_ENSURE_SUCCESS(rv, rv);
  1294     rv = stmt->Execute();
  1295     NS_ENSURE_SUCCESS(rv, rv);
  1298   // Addrefing and returning
  1299   NS_ADDREF(*retVal = dl);
  1300   return NS_OK;
  1303 nsresult
  1304 nsDownloadManager::AddToCurrentDownloads(nsDownload *aDl)
  1306   nsCOMArray<nsDownload>& currentDownloads =
  1307     aDl->mPrivate ? mCurrentPrivateDownloads : mCurrentDownloads;
  1308   if (!currentDownloads.AppendObject(aDl))
  1309     return NS_ERROR_OUT_OF_MEMORY;
  1311   aDl->mDownloadManager = this;
  1312   return NS_OK;
  1315 void
  1316 nsDownloadManager::SendEvent(nsDownload *aDownload, const char *aTopic)
  1318   (void)mObserverService->NotifyObservers(aDownload, aTopic, nullptr);
  1321 ////////////////////////////////////////////////////////////////////////////////
  1322 //// nsIDownloadManager
  1324 NS_IMETHODIMP
  1325 nsDownloadManager::GetActivePrivateDownloadCount(int32_t* aResult)
  1327   NS_ENSURE_STATE(!mUseJSTransfer);
  1329   *aResult = mCurrentPrivateDownloads.Count();
  1330   return NS_OK;
  1333 NS_IMETHODIMP
  1334 nsDownloadManager::GetActiveDownloadCount(int32_t *aResult)
  1336   NS_ENSURE_STATE(!mUseJSTransfer);
  1338   *aResult = mCurrentDownloads.Count();
  1340   return NS_OK;
  1343 NS_IMETHODIMP
  1344 nsDownloadManager::GetActiveDownloads(nsISimpleEnumerator **aResult)
  1346   NS_ENSURE_STATE(!mUseJSTransfer);
  1348   return NS_NewArrayEnumerator(aResult, mCurrentDownloads);
  1351 NS_IMETHODIMP
  1352 nsDownloadManager::GetActivePrivateDownloads(nsISimpleEnumerator **aResult)
  1354   NS_ENSURE_STATE(!mUseJSTransfer);
  1356   return NS_NewArrayEnumerator(aResult, mCurrentPrivateDownloads);
  1359 /**
  1360  * For platforms where helper apps use the downloads directory (i.e. mobile),
  1361  * this should be kept in sync with nsExternalHelperAppService.cpp
  1362  */
  1363 NS_IMETHODIMP
  1364 nsDownloadManager::GetDefaultDownloadsDirectory(nsIFile **aResult)
  1366   nsCOMPtr<nsIFile> downloadDir;
  1368   nsresult rv;
  1369   nsCOMPtr<nsIProperties> dirService =
  1370      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
  1371   NS_ENSURE_SUCCESS(rv, rv);
  1373   // OSX 10.4:
  1374   // Desktop
  1375   // OSX 10.5:
  1376   // User download directory
  1377   // Vista:
  1378   // Downloads
  1379   // XP/2K:
  1380   // My Documents/Downloads
  1381   // Linux:
  1382   // XDG user dir spec, with a fallback to Home/Downloads
  1384   nsXPIDLString folderName;
  1385   mBundle->GetStringFromName(MOZ_UTF16("downloadsFolder"),
  1386                              getter_Copies(folderName));
  1388 #if defined (XP_MACOSX)
  1389   rv = dirService->Get(NS_OSX_DEFAULT_DOWNLOAD_DIR,
  1390                        NS_GET_IID(nsIFile),
  1391                        getter_AddRefs(downloadDir));
  1392   NS_ENSURE_SUCCESS(rv, rv);
  1393 #elif defined(XP_WIN)
  1394   rv = dirService->Get(NS_WIN_DEFAULT_DOWNLOAD_DIR,
  1395                        NS_GET_IID(nsIFile),
  1396                        getter_AddRefs(downloadDir));
  1397   NS_ENSURE_SUCCESS(rv, rv);
  1399   // Check the os version
  1400   nsCOMPtr<nsIPropertyBag2> infoService =
  1401      do_GetService(NS_SYSTEMINFO_CONTRACTID, &rv);
  1402   NS_ENSURE_SUCCESS(rv, rv);
  1404   int32_t version;
  1405   NS_NAMED_LITERAL_STRING(osVersion, "version");
  1406   rv = infoService->GetPropertyAsInt32(osVersion, &version);
  1407   NS_ENSURE_SUCCESS(rv, rv);
  1408   if (version < 6) { // XP/2K
  1409     // First get "My Documents"
  1410     rv = dirService->Get(NS_WIN_PERSONAL_DIR,
  1411                          NS_GET_IID(nsIFile),
  1412                          getter_AddRefs(downloadDir));
  1413     NS_ENSURE_SUCCESS(rv, rv);
  1415     rv = downloadDir->Append(folderName);
  1416     NS_ENSURE_SUCCESS(rv, rv);
  1418     // This could be the first time we are creating the downloads folder in My
  1419     // Documents, so make sure it exists.
  1420     bool exists;
  1421     rv = downloadDir->Exists(&exists);
  1422     NS_ENSURE_SUCCESS(rv, rv);
  1423     if (!exists) {
  1424       rv = downloadDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
  1425       NS_ENSURE_SUCCESS(rv, rv);
  1428 #elif defined(XP_UNIX)
  1429 #if defined(MOZ_WIDGET_ANDROID)
  1430     // Android doesn't have a $HOME directory, and by default we only have
  1431     // write access to /data/data/org.mozilla.{$APP} and /sdcard
  1432     char* downloadDirPath = getenv("DOWNLOADS_DIRECTORY");
  1433     if (downloadDirPath) {
  1434       rv = NS_NewNativeLocalFile(nsDependentCString(downloadDirPath),
  1435                                  true, getter_AddRefs(downloadDir));
  1436       NS_ENSURE_SUCCESS(rv, rv);
  1438     else {
  1439       rv = NS_ERROR_FAILURE;
  1441 #else
  1442   rv = dirService->Get(NS_UNIX_DEFAULT_DOWNLOAD_DIR,
  1443                        NS_GET_IID(nsIFile),
  1444                        getter_AddRefs(downloadDir));
  1445   // fallback to Home/Downloads
  1446   if (NS_FAILED(rv)) {
  1447     rv = dirService->Get(NS_UNIX_HOME_DIR,
  1448                          NS_GET_IID(nsIFile),
  1449                          getter_AddRefs(downloadDir));
  1450     NS_ENSURE_SUCCESS(rv, rv);
  1451     rv = downloadDir->Append(folderName);
  1452     NS_ENSURE_SUCCESS(rv, rv);
  1454 #endif
  1455 #else
  1456   rv = dirService->Get(NS_OS_HOME_DIR,
  1457                        NS_GET_IID(nsIFile),
  1458                        getter_AddRefs(downloadDir));
  1459   NS_ENSURE_SUCCESS(rv, rv);
  1460   rv = downloadDir->Append(folderName);
  1461   NS_ENSURE_SUCCESS(rv, rv);
  1462 #endif
  1464   downloadDir.forget(aResult);
  1466   return NS_OK;
  1469 #define NS_BRANCH_DOWNLOAD     "browser.download."
  1470 #define NS_PREF_FOLDERLIST     "folderList"
  1471 #define NS_PREF_DIR            "dir"
  1473 NS_IMETHODIMP
  1474 nsDownloadManager::GetUserDownloadsDirectory(nsIFile **aResult)
  1476   nsresult rv;
  1477   nsCOMPtr<nsIProperties> dirService =
  1478      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
  1479   NS_ENSURE_SUCCESS(rv, rv);
  1481   nsCOMPtr<nsIPrefService> prefService =
  1482      do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  1483   NS_ENSURE_SUCCESS(rv, rv);
  1485   nsCOMPtr<nsIPrefBranch> prefBranch;
  1486   rv = prefService->GetBranch(NS_BRANCH_DOWNLOAD,
  1487                               getter_AddRefs(prefBranch));
  1488   NS_ENSURE_SUCCESS(rv, rv);
  1490   int32_t val;
  1491   rv = prefBranch->GetIntPref(NS_PREF_FOLDERLIST,
  1492                               &val);
  1493   NS_ENSURE_SUCCESS(rv, rv);
  1495   switch(val) {
  1496     case 0: // Desktop
  1498         nsCOMPtr<nsIFile> downloadDir;
  1499         nsCOMPtr<nsIProperties> dirService =
  1500            do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
  1501         NS_ENSURE_SUCCESS(rv, rv);
  1502         rv = dirService->Get(NS_OS_DESKTOP_DIR,
  1503                              NS_GET_IID(nsIFile),
  1504                              getter_AddRefs(downloadDir));
  1505         NS_ENSURE_SUCCESS(rv, rv);
  1506         downloadDir.forget(aResult);
  1507         return NS_OK;
  1509       break;
  1510     case 1: // Downloads
  1511       return GetDefaultDownloadsDirectory(aResult);
  1512     case 2: // Custom
  1514         nsCOMPtr<nsIFile> customDirectory;
  1515         prefBranch->GetComplexValue(NS_PREF_DIR,
  1516                                     NS_GET_IID(nsIFile),
  1517                                     getter_AddRefs(customDirectory));
  1518         if (customDirectory) {
  1519           bool exists = false;
  1520           (void)customDirectory->Exists(&exists);
  1522           if (!exists) {
  1523             rv = customDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
  1524             if (NS_SUCCEEDED(rv)) {
  1525               customDirectory.forget(aResult);
  1526               return NS_OK;
  1529             // Create failed, so it still doesn't exist.  Fall out and get the
  1530             // default downloads directory.
  1533           bool writable = false;
  1534           bool directory = false;
  1535           (void)customDirectory->IsWritable(&writable);
  1536           (void)customDirectory->IsDirectory(&directory);
  1538           if (exists && writable && directory) {
  1539             customDirectory.forget(aResult);
  1540             return NS_OK;
  1543         rv = GetDefaultDownloadsDirectory(aResult);
  1544         if (NS_SUCCEEDED(rv)) {
  1545           (void)prefBranch->SetComplexValue(NS_PREF_DIR,
  1546                                             NS_GET_IID(nsIFile),
  1547                                             *aResult);
  1549         return rv;
  1551       break;
  1553   return NS_ERROR_INVALID_ARG;
  1556 NS_IMETHODIMP
  1557 nsDownloadManager::AddDownload(DownloadType aDownloadType,
  1558                                nsIURI *aSource,
  1559                                nsIURI *aTarget,
  1560                                const nsAString& aDisplayName,
  1561                                nsIMIMEInfo *aMIMEInfo,
  1562                                PRTime aStartTime,
  1563                                nsIFile *aTempFile,
  1564                                nsICancelable *aCancelable,
  1565                                bool aIsPrivate,
  1566                                nsIDownload **aDownload)
  1568   NS_ENSURE_STATE(!mUseJSTransfer);
  1570   NS_ENSURE_ARG_POINTER(aSource);
  1571   NS_ENSURE_ARG_POINTER(aTarget);
  1572   NS_ENSURE_ARG_POINTER(aDownload);
  1574   nsresult rv;
  1576   // target must be on the local filesystem
  1577   nsCOMPtr<nsIFileURL> targetFileURL = do_QueryInterface(aTarget, &rv);
  1578   NS_ENSURE_SUCCESS(rv, rv);
  1580   nsCOMPtr<nsIFile> targetFile;
  1581   rv = targetFileURL->GetFile(getter_AddRefs(targetFile));
  1582   NS_ENSURE_SUCCESS(rv, rv);
  1584   nsRefPtr<nsDownload> dl = new nsDownload();
  1585   if (!dl)
  1586     return NS_ERROR_OUT_OF_MEMORY;
  1588   // give our new nsIDownload some info so it's ready to go off into the world
  1589   dl->mTarget = aTarget;
  1590   dl->mSource = aSource;
  1591   dl->mTempFile = aTempFile;
  1592   dl->mPrivate = aIsPrivate;
  1594   dl->mDisplayName = aDisplayName;
  1595   if (dl->mDisplayName.IsEmpty())
  1596     targetFile->GetLeafName(dl->mDisplayName);
  1598   dl->mMIMEInfo = aMIMEInfo;
  1599   dl->SetStartTime(aStartTime == 0 ? PR_Now() : aStartTime);
  1601   // Creates a cycle that will be broken when the download finishes
  1602   dl->mCancelable = aCancelable;
  1604   // Adding to the DB
  1605   nsAutoCString source, target;
  1606   aSource->GetSpec(source);
  1607   aTarget->GetSpec(target);
  1609   // Track the temp file for exthandler downloads
  1610   nsAutoString tempPath;
  1611   if (aTempFile)
  1612     aTempFile->GetPath(tempPath);
  1614   // Break down MIMEInfo but don't panic if we can't get all the pieces - we
  1615   // can still download the file
  1616   nsAutoCString persistentDescriptor, mimeType;
  1617   nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
  1618   if (aMIMEInfo) {
  1619     (void)aMIMEInfo->GetType(mimeType);
  1621     nsCOMPtr<nsIHandlerApp> handlerApp;
  1622     (void)aMIMEInfo->GetPreferredApplicationHandler(getter_AddRefs(handlerApp));
  1623     nsCOMPtr<nsILocalHandlerApp> locHandlerApp = do_QueryInterface(handlerApp);
  1625     if (locHandlerApp) {
  1626       nsCOMPtr<nsIFile> executable;
  1627       (void)locHandlerApp->GetExecutable(getter_AddRefs(executable));
  1628       (void)executable->GetPersistentDescriptor(persistentDescriptor);
  1631     (void)aMIMEInfo->GetPreferredAction(&action);
  1634   int64_t id = AddDownloadToDB(dl->mDisplayName, source, target, tempPath,
  1635                                dl->mStartTime, dl->mLastUpdate,
  1636                                mimeType, persistentDescriptor, action,
  1637                                dl->mPrivate, dl->mGUID /* outparam */);
  1638   NS_ENSURE_TRUE(id, NS_ERROR_FAILURE);
  1639   dl->mID = id;
  1641   rv = AddToCurrentDownloads(dl);
  1642   (void)dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED);
  1643   NS_ENSURE_SUCCESS(rv, rv);
  1645 #ifdef DOWNLOAD_SCANNER
  1646   if (mScanner) {
  1647     bool scan = true;
  1648     nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
  1649     if (prefs) {
  1650       (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan);
  1652     // We currently apply local security policy to downloads when we scan
  1653     // via windows all-in-one download security api. The CheckPolicy call
  1654     // below is a pre-emptive part of that process. So tie applying security
  1655     // zone policy settings when downloads are intiated to the same pref
  1656     // that triggers applying security zone policy settings after a download
  1657     // completes. (bug 504804)
  1658     if (scan) {
  1659       AVCheckPolicyState res = mScanner->CheckPolicy(aSource, aTarget);
  1660       if (res == AVPOLICY_BLOCKED) {
  1661         // This download will get deleted during a call to IAE's Save,
  1662         // so go ahead and mark it as blocked and avoid the download.
  1663         (void)CancelDownload(id);
  1664         (void)dl->SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY);
  1668 #endif
  1670   // Check with parental controls to see if file downloads
  1671   // are allowed for this user. If not allowed, cancel the
  1672   // download and mark its state as being blocked.
  1673   nsCOMPtr<nsIParentalControlsService> pc =
  1674     do_CreateInstance(NS_PARENTALCONTROLSSERVICE_CONTRACTID);
  1675   if (pc) {
  1676     bool enabled = false;
  1677     (void)pc->GetBlockFileDownloadsEnabled(&enabled);
  1678     if (enabled) {
  1679       (void)CancelDownload(id);
  1680       (void)dl->SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL);
  1683     // Log the event if required by pc settings.
  1684     bool logEnabled = false;
  1685     (void)pc->GetLoggingEnabled(&logEnabled);
  1686     if (logEnabled) {
  1687       (void)pc->Log(nsIParentalControlsService::ePCLog_FileDownload,
  1688                     enabled,
  1689                     aSource,
  1690                     nullptr);
  1694   NS_ADDREF(*aDownload = dl);
  1696   return NS_OK;
  1699 NS_IMETHODIMP
  1700 nsDownloadManager::GetDownload(uint32_t aID, nsIDownload **aDownloadItem)
  1702   NS_ENSURE_STATE(!mUseJSTransfer);
  1704   NS_WARNING("Using integer IDs without compat mode enabled");
  1706   nsDownload *itm = FindDownload(aID);
  1708   nsRefPtr<nsDownload> dl;
  1709   if (!itm) {
  1710     nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
  1711     NS_ENSURE_SUCCESS(rv, rv);
  1713     itm = dl.get();
  1716   NS_ADDREF(*aDownloadItem = itm);
  1718   return NS_OK;
  1721 namespace {
  1722 class AsyncResult : public nsRunnable
  1724 public:
  1725   AsyncResult(nsresult aStatus, nsIDownload* aResult,
  1726               nsIDownloadManagerResult* aCallback)
  1727   : mStatus(aStatus), mResult(aResult), mCallback(aCallback)
  1731   NS_IMETHOD Run()
  1733     mCallback->HandleResult(mStatus, mResult);
  1734     return NS_OK;
  1737 private:
  1738   nsresult mStatus;
  1739   nsCOMPtr<nsIDownload> mResult;
  1740   nsCOMPtr<nsIDownloadManagerResult> mCallback;
  1741 };
  1742 } // anonymous namespace
  1744 NS_IMETHODIMP
  1745 nsDownloadManager::GetDownloadByGUID(const nsACString& aGUID,
  1746                                      nsIDownloadManagerResult* aCallback)
  1748   NS_ENSURE_STATE(!mUseJSTransfer);
  1750   nsDownload *itm = FindDownload(aGUID);
  1752   nsresult rv = NS_OK;
  1753   nsRefPtr<nsDownload> dl;
  1754   if (!itm) {
  1755     rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl));
  1756     itm = dl.get();
  1759   nsRefPtr<AsyncResult> runnable = new AsyncResult(rv, itm, aCallback);
  1760   NS_DispatchToMainThread(runnable);
  1761   return NS_OK;
  1764 nsDownload *
  1765 nsDownloadManager::FindDownload(uint32_t aID)
  1767   // we shouldn't ever have many downloads, so we can loop over them
  1768   for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
  1769     nsDownload *dl = mCurrentDownloads[i];
  1770     if (dl->mID == aID)
  1771       return dl;
  1774   return nullptr;
  1777 nsDownload *
  1778 nsDownloadManager::FindDownload(const nsACString& aGUID)
  1780   // we shouldn't ever have many downloads, so we can loop over them
  1781   for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
  1782     nsDownload *dl = mCurrentDownloads[i];
  1783     if (dl->mGUID == aGUID)
  1784       return dl;
  1787   for (int32_t i = mCurrentPrivateDownloads.Count() - 1; i >= 0; --i) {
  1788     nsDownload *dl = mCurrentPrivateDownloads[i];
  1789     if (dl->mGUID == aGUID)
  1790       return dl;
  1793   return nullptr;
  1796 NS_IMETHODIMP
  1797 nsDownloadManager::CancelDownload(uint32_t aID)
  1799   NS_ENSURE_STATE(!mUseJSTransfer);
  1801   NS_WARNING("Using integer IDs without compat mode enabled");
  1803   // We AddRef here so we don't lose access to member variables when we remove
  1804   nsRefPtr<nsDownload> dl = FindDownload(aID);
  1806   // if it's null, someone passed us a bad id.
  1807   if (!dl)
  1808     return NS_ERROR_FAILURE;
  1810   return dl->Cancel();
  1813 nsresult
  1814 nsDownloadManager::RetryDownload(const nsACString& aGUID)
  1816   nsRefPtr<nsDownload> dl;
  1817   nsresult rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl));
  1818   NS_ENSURE_SUCCESS(rv, rv);
  1820   return RetryDownload(dl);
  1824 NS_IMETHODIMP
  1825 nsDownloadManager::RetryDownload(uint32_t aID)
  1827   NS_ENSURE_STATE(!mUseJSTransfer);
  1829   NS_WARNING("Using integer IDs without compat mode enabled");
  1831   nsRefPtr<nsDownload> dl;
  1832   nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
  1833   NS_ENSURE_SUCCESS(rv, rv);
  1835   return RetryDownload(dl);
  1838 nsresult
  1839 nsDownloadManager::RetryDownload(nsDownload* dl)
  1841   // if our download is not canceled or failed, we should fail
  1842   if (dl->mDownloadState != nsIDownloadManager::DOWNLOAD_FAILED &&
  1843       dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL &&
  1844       dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY &&
  1845       dl->mDownloadState != nsIDownloadManager::DOWNLOAD_DIRTY &&
  1846       dl->mDownloadState != nsIDownloadManager::DOWNLOAD_CANCELED)
  1847     return NS_ERROR_FAILURE;
  1849   // If the download has failed and is resumable then we first try resuming it
  1850   nsresult rv;
  1851   if (dl->mDownloadState == nsIDownloadManager::DOWNLOAD_FAILED && dl->IsResumable()) {
  1852     rv = dl->Resume();
  1853     if (NS_SUCCEEDED(rv))
  1854       return rv;
  1857   // reset time and download progress
  1858   dl->SetStartTime(PR_Now());
  1859   dl->SetProgressBytes(0, -1);
  1861   nsCOMPtr<nsIWebBrowserPersist> wbp =
  1862     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
  1863   NS_ENSURE_SUCCESS(rv, rv);
  1865   rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES |
  1866                             nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
  1867   NS_ENSURE_SUCCESS(rv, rv);
  1869   rv = AddToCurrentDownloads(dl);
  1870   NS_ENSURE_SUCCESS(rv, rv);
  1872   rv = dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED);
  1873   NS_ENSURE_SUCCESS(rv, rv);
  1875   // Creates a cycle that will be broken when the download finishes
  1876   dl->mCancelable = wbp;
  1877   (void)wbp->SetProgressListener(dl);
  1879   rv = wbp->SavePrivacyAwareURI(dl->mSource, nullptr, nullptr, nullptr, nullptr,
  1880                                 dl->mTarget, dl->mPrivate);
  1881   if (NS_FAILED(rv)) {
  1882     dl->mCancelable = nullptr;
  1883     (void)wbp->SetProgressListener(nullptr);
  1884     return rv;
  1887   return NS_OK;
  1890 static nsresult
  1891 RemoveDownloadByGUID(const nsACString& aGUID, mozIStorageConnection* aDBConn)
  1893   nsCOMPtr<mozIStorageStatement> stmt;
  1894   nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
  1895     "DELETE FROM moz_downloads "
  1896     "WHERE guid = :guid"), getter_AddRefs(stmt));
  1897   NS_ENSURE_SUCCESS(rv, rv);
  1899   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
  1900   NS_ENSURE_SUCCESS(rv, rv);
  1902   rv = stmt->Execute();
  1903   NS_ENSURE_SUCCESS(rv, rv);
  1905   return NS_OK;
  1908 nsresult
  1909 nsDownloadManager::RemoveDownload(const nsACString& aGUID)
  1911   nsRefPtr<nsDownload> dl = FindDownload(aGUID);
  1912   MOZ_ASSERT(!dl, "Can't call RemoveDownload on a download in progress!");
  1913   if (dl)
  1914     return NS_ERROR_FAILURE;
  1916   nsresult rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl));
  1917   NS_ENSURE_SUCCESS(rv, rv);
  1919   if (dl->mPrivate) {
  1920     RemoveDownloadByGUID(aGUID, mPrivateDBConn);
  1921   } else {
  1922     RemoveDownloadByGUID(aGUID, mDBConn);
  1925   return NotifyDownloadRemoval(dl);
  1928 NS_IMETHODIMP
  1929 nsDownloadManager::RemoveDownload(uint32_t aID)
  1931   NS_ENSURE_STATE(!mUseJSTransfer);
  1933   NS_WARNING("Using integer IDs without compat mode enabled");
  1935   nsRefPtr<nsDownload> dl = FindDownload(aID);
  1936   MOZ_ASSERT(!dl, "Can't call RemoveDownload on a download in progress!");
  1937   if (dl)
  1938     return NS_ERROR_FAILURE;
  1940   nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
  1941   NS_ENSURE_SUCCESS(rv, rv);
  1943   nsCOMPtr<mozIStorageStatement> stmt;
  1944   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
  1945     "DELETE FROM moz_downloads "
  1946     "WHERE id = :id"), getter_AddRefs(stmt));
  1947   NS_ENSURE_SUCCESS(rv, rv);
  1949   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID); // unsigned; 64-bit to prevent overflow
  1950   NS_ENSURE_SUCCESS(rv, rv);
  1952   rv = stmt->Execute();
  1953   NS_ENSURE_SUCCESS(rv, rv);
  1955   // Notify the UI with the topic and download id
  1956   return NotifyDownloadRemoval(dl);
  1959 nsresult
  1960 nsDownloadManager::NotifyDownloadRemoval(nsDownload* aRemoved)
  1962   nsCOMPtr<nsISupportsPRUint32> id;
  1963   nsCOMPtr<nsISupportsCString> guid;
  1964   nsresult rv;
  1966   // Only send an integer ID notification if the download is public.
  1967   bool sendDeprecatedNotification = !(aRemoved && aRemoved->mPrivate);
  1969   if (sendDeprecatedNotification && aRemoved) {
  1970     id = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
  1971     NS_ENSURE_SUCCESS(rv, rv);
  1972     uint32_t dlID;
  1973     rv = aRemoved->GetId(&dlID);
  1974     NS_ENSURE_SUCCESS(rv, rv);
  1975     rv = id->SetData(dlID);
  1976     NS_ENSURE_SUCCESS(rv, rv);
  1979   if (sendDeprecatedNotification) {
  1980     mObserverService->NotifyObservers(id,
  1981                                      "download-manager-remove-download",
  1982                                      nullptr);
  1985   if (aRemoved) {
  1986     guid = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
  1987     NS_ENSURE_SUCCESS(rv, rv);
  1988     nsAutoCString guidStr;
  1989     rv = aRemoved->GetGuid(guidStr);
  1990     NS_ENSURE_SUCCESS(rv, rv);
  1991     rv = guid->SetData(guidStr);
  1992     NS_ENSURE_SUCCESS(rv, rv);
  1995   mObserverService->NotifyObservers(guid,
  1996                                     "download-manager-remove-download-guid",
  1997                                     nullptr);
  1998   return NS_OK;
  2001 static nsresult
  2002 DoRemoveDownloadsByTimeframe(mozIStorageConnection* aDBConn,
  2003                              int64_t aStartTime,
  2004                              int64_t aEndTime)
  2006   nsCOMPtr<mozIStorageStatement> stmt;
  2007   nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
  2008     "DELETE FROM moz_downloads "
  2009     "WHERE startTime >= :startTime "
  2010     "AND startTime <= :endTime "
  2011     "AND state NOT IN (:downloading, :paused, :queued)"), getter_AddRefs(stmt));
  2012   NS_ENSURE_SUCCESS(rv, rv);
  2014   // Bind the times
  2015   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
  2016   NS_ENSURE_SUCCESS(rv, rv);
  2017   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime);
  2018   NS_ENSURE_SUCCESS(rv, rv);
  2020   // Bind the active states
  2021   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING);
  2022   NS_ENSURE_SUCCESS(rv, rv);
  2023   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("paused"), nsIDownloadManager::DOWNLOAD_PAUSED);
  2024   NS_ENSURE_SUCCESS(rv, rv);
  2025   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
  2026   NS_ENSURE_SUCCESS(rv, rv);
  2028   // Execute
  2029   rv = stmt->Execute();
  2030   NS_ENSURE_SUCCESS(rv, rv);
  2032   return NS_OK;
  2035 NS_IMETHODIMP
  2036 nsDownloadManager::RemoveDownloadsByTimeframe(int64_t aStartTime,
  2037                                               int64_t aEndTime)
  2039   NS_ENSURE_STATE(!mUseJSTransfer);
  2041   nsresult rv = DoRemoveDownloadsByTimeframe(mDBConn, aStartTime, aEndTime);
  2042   nsresult rv2 = DoRemoveDownloadsByTimeframe(mPrivateDBConn, aStartTime, aEndTime);
  2043   NS_ENSURE_SUCCESS(rv, rv);
  2044   NS_ENSURE_SUCCESS(rv2, rv2);
  2046   // Notify the UI with the topic and null subject to indicate "remove multiple"
  2047   return NotifyDownloadRemoval(nullptr);
  2050 NS_IMETHODIMP
  2051 nsDownloadManager::CleanUp()
  2053   NS_ENSURE_STATE(!mUseJSTransfer);
  2055   return CleanUp(mDBConn);
  2058 NS_IMETHODIMP
  2059 nsDownloadManager::CleanUpPrivate()
  2061   NS_ENSURE_STATE(!mUseJSTransfer);
  2063   return CleanUp(mPrivateDBConn);
  2066 nsresult
  2067 nsDownloadManager::CleanUp(mozIStorageConnection* aDBConn)
  2069   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
  2070                              nsIDownloadManager::DOWNLOAD_FAILED,
  2071                              nsIDownloadManager::DOWNLOAD_CANCELED,
  2072                              nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
  2073                              nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
  2074                              nsIDownloadManager::DOWNLOAD_DIRTY };
  2076   nsCOMPtr<mozIStorageStatement> stmt;
  2077   nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
  2078     "DELETE FROM moz_downloads "
  2079     "WHERE state = ? "
  2080       "OR state = ? "
  2081       "OR state = ? "
  2082       "OR state = ? "
  2083       "OR state = ? "
  2084       "OR state = ?"), getter_AddRefs(stmt));
  2085   NS_ENSURE_SUCCESS(rv, rv);
  2086   for (uint32_t i = 0; i < ArrayLength(states); ++i) {
  2087     rv = stmt->BindInt32ByIndex(i, states[i]);
  2088     NS_ENSURE_SUCCESS(rv, rv);
  2091   rv = stmt->Execute();
  2092   NS_ENSURE_SUCCESS(rv, rv);
  2094   // Notify the UI with the topic and null subject to indicate "remove multiple"
  2095   return NotifyDownloadRemoval(nullptr);
  2098 static nsresult
  2099 DoGetCanCleanUp(mozIStorageConnection* aDBConn, bool *aResult)
  2101   // This method should never return anything but NS_OK for the benefit of
  2102   // unwitting consumers.
  2104   *aResult = false;
  2106   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
  2107                              nsIDownloadManager::DOWNLOAD_FAILED,
  2108                              nsIDownloadManager::DOWNLOAD_CANCELED,
  2109                              nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
  2110                              nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
  2111                              nsIDownloadManager::DOWNLOAD_DIRTY };
  2113   nsCOMPtr<mozIStorageStatement> stmt;
  2114   nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
  2115     "SELECT COUNT(*) "
  2116     "FROM moz_downloads "
  2117     "WHERE state = ? "
  2118       "OR state = ? "
  2119       "OR state = ? "
  2120       "OR state = ? "
  2121       "OR state = ? "
  2122       "OR state = ?"), getter_AddRefs(stmt));
  2123   NS_ENSURE_SUCCESS(rv, NS_OK);
  2124   for (uint32_t i = 0; i < ArrayLength(states); ++i) {
  2125     rv = stmt->BindInt32ByIndex(i, states[i]);
  2126     NS_ENSURE_SUCCESS(rv, NS_OK);
  2129   bool moreResults; // We don't really care...
  2130   rv = stmt->ExecuteStep(&moreResults);
  2131   NS_ENSURE_SUCCESS(rv, NS_OK);
  2133   int32_t count;
  2134   rv = stmt->GetInt32(0, &count);
  2135   NS_ENSURE_SUCCESS(rv, NS_OK);
  2137   if (count > 0)
  2138     *aResult = true;
  2140   return NS_OK;
  2143 NS_IMETHODIMP
  2144 nsDownloadManager::GetCanCleanUp(bool *aResult)
  2146   NS_ENSURE_STATE(!mUseJSTransfer);
  2148   return DoGetCanCleanUp(mDBConn, aResult);
  2151 NS_IMETHODIMP
  2152 nsDownloadManager::GetCanCleanUpPrivate(bool *aResult)
  2154   NS_ENSURE_STATE(!mUseJSTransfer);
  2156   return DoGetCanCleanUp(mPrivateDBConn, aResult);
  2159 NS_IMETHODIMP
  2160 nsDownloadManager::PauseDownload(uint32_t aID)
  2162   NS_ENSURE_STATE(!mUseJSTransfer);
  2164   NS_WARNING("Using integer IDs without compat mode enabled");
  2166   nsDownload *dl = FindDownload(aID);
  2167   if (!dl)
  2168     return NS_ERROR_FAILURE;
  2170   return dl->Pause();
  2173 NS_IMETHODIMP
  2174 nsDownloadManager::ResumeDownload(uint32_t aID)
  2176   NS_ENSURE_STATE(!mUseJSTransfer);
  2178   NS_WARNING("Using integer IDs without compat mode enabled");
  2180   nsDownload *dl = FindDownload(aID);
  2181   if (!dl)
  2182     return NS_ERROR_FAILURE;
  2184   return dl->Resume();
  2187 NS_IMETHODIMP
  2188 nsDownloadManager::GetDBConnection(mozIStorageConnection **aDBConn)
  2190   NS_ENSURE_STATE(!mUseJSTransfer);
  2192   NS_ADDREF(*aDBConn = mDBConn);
  2194   return NS_OK;
  2197 NS_IMETHODIMP
  2198 nsDownloadManager::GetPrivateDBConnection(mozIStorageConnection **aDBConn)
  2200   NS_ENSURE_STATE(!mUseJSTransfer);
  2202   NS_ADDREF(*aDBConn = mPrivateDBConn);
  2204   return NS_OK;
  2207 NS_IMETHODIMP
  2208 nsDownloadManager::AddListener(nsIDownloadProgressListener *aListener)
  2210   NS_ENSURE_STATE(!mUseJSTransfer);
  2212   mListeners.AppendObject(aListener);
  2213   return NS_OK;
  2216 NS_IMETHODIMP
  2217 nsDownloadManager::AddPrivacyAwareListener(nsIDownloadProgressListener *aListener)
  2219   NS_ENSURE_STATE(!mUseJSTransfer);
  2221   mPrivacyAwareListeners.AppendObject(aListener);
  2222   return NS_OK;
  2225 NS_IMETHODIMP
  2226 nsDownloadManager::RemoveListener(nsIDownloadProgressListener *aListener)
  2228   NS_ENSURE_STATE(!mUseJSTransfer);
  2230   mListeners.RemoveObject(aListener);
  2231   mPrivacyAwareListeners.RemoveObject(aListener);
  2232   return NS_OK;
  2235 void
  2236 nsDownloadManager::NotifyListenersOnDownloadStateChange(int16_t aOldState,
  2237                                                         nsDownload *aDownload)
  2239   for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
  2240     mPrivacyAwareListeners[i]->OnDownloadStateChange(aOldState, aDownload);
  2243   // Only privacy-aware listeners should receive notifications about private
  2244   // downloads, while non-privacy-aware listeners receive no sign they exist.
  2245   if (aDownload->mPrivate) {
  2246     return;
  2249   for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
  2250     mListeners[i]->OnDownloadStateChange(aOldState, aDownload);
  2254 void
  2255 nsDownloadManager::NotifyListenersOnProgressChange(nsIWebProgress *aProgress,
  2256                                                    nsIRequest *aRequest,
  2257                                                    int64_t aCurSelfProgress,
  2258                                                    int64_t aMaxSelfProgress,
  2259                                                    int64_t aCurTotalProgress,
  2260                                                    int64_t aMaxTotalProgress,
  2261                                                    nsDownload *aDownload)
  2263   for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
  2264     mPrivacyAwareListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
  2265                                                 aMaxSelfProgress, aCurTotalProgress,
  2266                                                 aMaxTotalProgress, aDownload);
  2269   // Only privacy-aware listeners should receive notifications about private
  2270   // downloads, while non-privacy-aware listeners receive no sign they exist.
  2271   if (aDownload->mPrivate) {
  2272     return;
  2275   for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
  2276     mListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
  2277                                     aMaxSelfProgress, aCurTotalProgress,
  2278                                     aMaxTotalProgress, aDownload);
  2282 void
  2283 nsDownloadManager::NotifyListenersOnStateChange(nsIWebProgress *aProgress,
  2284                                                 nsIRequest *aRequest,
  2285                                                 uint32_t aStateFlags,
  2286                                                 nsresult aStatus,
  2287                                                 nsDownload *aDownload)
  2289   for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
  2290     mPrivacyAwareListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus,
  2291                                              aDownload);
  2294   // Only privacy-aware listeners should receive notifications about private
  2295   // downloads, while non-privacy-aware listeners receive no sign they exist.
  2296   if (aDownload->mPrivate) {
  2297     return;
  2300   for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
  2301     mListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus,
  2302                                  aDownload);
  2306 ////////////////////////////////////////////////////////////////////////////////
  2307 //// nsINavHistoryObserver
  2309 NS_IMETHODIMP
  2310 nsDownloadManager::OnBeginUpdateBatch()
  2312   // This method in not normally invoked when mUseJSTransfer is enabled, however
  2313   // we provide an extra check in case it is called manually by add-ons.
  2314   NS_ENSURE_STATE(!mUseJSTransfer);
  2316   // We already have a transaction, so don't make another
  2317   if (mHistoryTransaction)
  2318     return NS_OK;
  2320   // Start a transaction that commits when deleted
  2321   mHistoryTransaction = new mozStorageTransaction(mDBConn, true);
  2323   return NS_OK;
  2326 NS_IMETHODIMP
  2327 nsDownloadManager::OnEndUpdateBatch()
  2329   // Get rid of the transaction and cause it to commit
  2330   mHistoryTransaction = nullptr;
  2332   return NS_OK;
  2335 NS_IMETHODIMP
  2336 nsDownloadManager::OnVisit(nsIURI *aURI, int64_t aVisitID, PRTime aTime,
  2337                            int64_t aSessionID, int64_t aReferringID,
  2338                            uint32_t aTransitionType, const nsACString& aGUID,
  2339                            bool aHidden)
  2341   return NS_OK;
  2344 NS_IMETHODIMP
  2345 nsDownloadManager::OnTitleChanged(nsIURI *aURI,
  2346                                   const nsAString &aPageTitle,
  2347                                   const nsACString &aGUID)
  2349   return NS_OK;
  2352 NS_IMETHODIMP
  2353 nsDownloadManager::OnFrecencyChanged(nsIURI* aURI,
  2354                                      int32_t aNewFrecency,
  2355                                      const nsACString& aGUID,
  2356                                      bool aHidden,
  2357                                      PRTime aLastVisitDate)
  2359   return NS_OK;
  2362 NS_IMETHODIMP
  2363 nsDownloadManager::OnManyFrecenciesChanged()
  2365   return NS_OK;
  2368 NS_IMETHODIMP
  2369 nsDownloadManager::OnDeleteURI(nsIURI *aURI,
  2370                                const nsACString& aGUID,
  2371                                uint16_t aReason)
  2373   // This method in not normally invoked when mUseJSTransfer is enabled, however
  2374   // we provide an extra check in case it is called manually by add-ons.
  2375   NS_ENSURE_STATE(!mUseJSTransfer);
  2377   nsresult rv = RemoveDownloadsForURI(mGetIdsForURIStatement, aURI);
  2378   nsresult rv2 = RemoveDownloadsForURI(mGetPrivateIdsForURIStatement, aURI);
  2379   NS_ENSURE_SUCCESS(rv, rv);
  2380   NS_ENSURE_SUCCESS(rv2, rv2);
  2381   return NS_OK;
  2384 NS_IMETHODIMP
  2385 nsDownloadManager::OnClearHistory()
  2387   return CleanUp();
  2390 NS_IMETHODIMP
  2391 nsDownloadManager::OnPageChanged(nsIURI *aURI,
  2392                                  uint32_t aChangedAttribute,
  2393                                  const nsAString& aNewValue,
  2394                                  const nsACString &aGUID)
  2396   return NS_OK;
  2399 NS_IMETHODIMP
  2400 nsDownloadManager::OnDeleteVisits(nsIURI *aURI, PRTime aVisitTime,
  2401                                   const nsACString& aGUID,
  2402                                   uint16_t aReason, uint32_t aTransitionType)
  2404   // Don't bother removing downloads until the page is removed.
  2405   return NS_OK;
  2408 ////////////////////////////////////////////////////////////////////////////////
  2409 //// nsIObserver
  2411 NS_IMETHODIMP
  2412 nsDownloadManager::Observe(nsISupports *aSubject,
  2413                            const char *aTopic,
  2414                            const char16_t *aData)
  2416   // This method in not normally invoked when mUseJSTransfer is enabled, however
  2417   // we provide an extra check in case it is called manually by add-ons.
  2418   NS_ENSURE_STATE(!mUseJSTransfer);
  2420   // We need to count the active public downloads that could be lost
  2421   // by quitting, and add any active private ones as well, since per-window
  2422   // private browsing may be active.
  2423   int32_t currDownloadCount = mCurrentDownloads.Count();
  2425   // If we don't need to cancel all the downloads on quit, only count the ones
  2426   // that aren't resumable.
  2427   if (GetQuitBehavior() != QUIT_AND_CANCEL) {
  2428     for (int32_t i = currDownloadCount - 1; i >= 0; --i) {
  2429       if (mCurrentDownloads[i]->IsResumable()) {
  2430         currDownloadCount--;
  2434     // We have a count of the public, non-resumable downloads. Now we need
  2435     // to add the total number of private downloads, since they are in danger
  2436     // of being lost.
  2437     currDownloadCount += mCurrentPrivateDownloads.Count();
  2440   nsresult rv;
  2441   if (strcmp(aTopic, "oncancel") == 0) {
  2442     nsCOMPtr<nsIDownload> dl = do_QueryInterface(aSubject, &rv);
  2443     NS_ENSURE_SUCCESS(rv, rv);
  2445     dl->Cancel();
  2446   } else if (strcmp(aTopic, "profile-before-change") == 0) {
  2447     CloseAllDBs();
  2448   } else if (strcmp(aTopic, "quit-application") == 0) {
  2449     // Try to pause all downloads and, if appropriate, mark them as auto-resume
  2450     // unless user has specified that downloads should be canceled
  2451     enum QuitBehavior behavior = GetQuitBehavior();
  2452     if (behavior != QUIT_AND_CANCEL)
  2453       (void)PauseAllDownloads(bool(behavior != QUIT_AND_PAUSE));
  2455     // Remove downloads to break cycles and cancel downloads
  2456     (void)RemoveAllDownloads();
  2458    // Now that active downloads have been canceled, remove all completed or
  2459    // aborted downloads if the user's retention policy specifies it.
  2460     if (GetRetentionBehavior() == 1)
  2461       CleanUp();
  2462   } else if (strcmp(aTopic, "quit-application-requested") == 0 &&
  2463              currDownloadCount) {
  2464     nsCOMPtr<nsISupportsPRBool> cancelDownloads =
  2465       do_QueryInterface(aSubject, &rv);
  2466     NS_ENSURE_SUCCESS(rv, rv);
  2467 #ifndef XP_MACOSX
  2468     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
  2469                            MOZ_UTF16("quitCancelDownloadsAlertTitle"),
  2470                            MOZ_UTF16("quitCancelDownloadsAlertMsgMultiple"),
  2471                            MOZ_UTF16("quitCancelDownloadsAlertMsg"),
  2472                            MOZ_UTF16("dontQuitButtonWin"));
  2473 #else
  2474     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
  2475                            MOZ_UTF16("quitCancelDownloadsAlertTitle"),
  2476                            MOZ_UTF16("quitCancelDownloadsAlertMsgMacMultiple"),
  2477                            MOZ_UTF16("quitCancelDownloadsAlertMsgMac"),
  2478                            MOZ_UTF16("dontQuitButtonMac"));
  2479 #endif
  2480   } else if (strcmp(aTopic, "offline-requested") == 0 && currDownloadCount) {
  2481     nsCOMPtr<nsISupportsPRBool> cancelDownloads =
  2482       do_QueryInterface(aSubject, &rv);
  2483     NS_ENSURE_SUCCESS(rv, rv);
  2484     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
  2485                            MOZ_UTF16("offlineCancelDownloadsAlertTitle"),
  2486                            MOZ_UTF16("offlineCancelDownloadsAlertMsgMultiple"),
  2487                            MOZ_UTF16("offlineCancelDownloadsAlertMsg"),
  2488                            MOZ_UTF16("dontGoOfflineButton"));
  2490   else if (strcmp(aTopic, NS_IOSERVICE_GOING_OFFLINE_TOPIC) == 0) {
  2491     // Pause all downloads, and mark them to auto-resume.
  2492     (void)PauseAllDownloads(true);
  2494   else if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0 &&
  2495            nsDependentString(aData).EqualsLiteral(NS_IOSERVICE_ONLINE)) {
  2496     // We can now resume all downloads that are supposed to auto-resume.
  2497     (void)ResumeAllDownloads(false);
  2499   else if (strcmp(aTopic, "alertclickcallback") == 0) {
  2500     nsCOMPtr<nsIDownloadManagerUI> dmui =
  2501       do_GetService("@mozilla.org/download-manager-ui;1", &rv);
  2502     NS_ENSURE_SUCCESS(rv, rv);
  2503     return dmui->Show(nullptr, nullptr, nsIDownloadManagerUI::REASON_USER_INTERACTED,
  2504                       aData && NS_strcmp(aData, MOZ_UTF16("private")) == 0);
  2505   } else if (strcmp(aTopic, "sleep_notification") == 0 ||
  2506              strcmp(aTopic, "suspend_process_notification") == 0) {
  2507     // Pause downloads if we're sleeping, and mark the downloads as auto-resume
  2508     (void)PauseAllDownloads(true);
  2509   } else if (strcmp(aTopic, "wake_notification") == 0 ||
  2510              strcmp(aTopic, "resume_process_notification") == 0) {
  2511     int32_t resumeOnWakeDelay = 10000;
  2512     nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
  2513     if (pref)
  2514       (void)pref->GetIntPref(PREF_BDM_RESUMEONWAKEDELAY, &resumeOnWakeDelay);
  2516     // Wait a little bit before trying to resume to avoid resuming when network
  2517     // connections haven't restarted yet
  2518     mResumeOnWakeTimer = do_CreateInstance("@mozilla.org/timer;1");
  2519     if (resumeOnWakeDelay >= 0 && mResumeOnWakeTimer) {
  2520       (void)mResumeOnWakeTimer->InitWithFuncCallback(ResumeOnWakeCallback,
  2521         this, resumeOnWakeDelay, nsITimer::TYPE_ONE_SHOT);
  2523   } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
  2524     // Upon leaving private browsing mode, cancel all private downloads,
  2525     // remove all trace of them, and then blow away the private database
  2526     // and recreate a blank one.
  2527     RemoveAllDownloads(mCurrentPrivateDownloads);
  2528     InitPrivateDB();
  2529   } else if (strcmp(aTopic, "last-pb-context-exiting") == 0) {
  2530     // If there are active private downloads, prompt the user to confirm leaving
  2531     // private browsing mode (thereby cancelling them). Otherwise, silently proceed.
  2532     if (!mCurrentPrivateDownloads.Count())
  2533       return NS_OK;
  2535     nsCOMPtr<nsISupportsPRBool> cancelDownloads = do_QueryInterface(aSubject, &rv);
  2536     NS_ENSURE_SUCCESS(rv, rv);
  2538     ConfirmCancelDownloads(mCurrentPrivateDownloads.Count(), cancelDownloads,
  2539                            MOZ_UTF16("leavePrivateBrowsingCancelDownloadsAlertTitle"),
  2540                            MOZ_UTF16("leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple"),
  2541                            MOZ_UTF16("leavePrivateBrowsingWindowsCancelDownloadsAlertMsg"),
  2542                            MOZ_UTF16("dontLeavePrivateBrowsingButton"));
  2545   return NS_OK;
  2548 void
  2549 nsDownloadManager::ConfirmCancelDownloads(int32_t aCount,
  2550                                           nsISupportsPRBool *aCancelDownloads,
  2551                                           const char16_t *aTitle,
  2552                                           const char16_t *aCancelMessageMultiple,
  2553                                           const char16_t *aCancelMessageSingle,
  2554                                           const char16_t *aDontCancelButton)
  2556   // If user has already dismissed quit request, then do nothing
  2557   bool quitRequestCancelled = false;
  2558   aCancelDownloads->GetData(&quitRequestCancelled);
  2559   if (quitRequestCancelled)
  2560     return;
  2562   nsXPIDLString title, message, quitButton, dontQuitButton;
  2564   mBundle->GetStringFromName(aTitle, getter_Copies(title));
  2566   nsAutoString countString;
  2567   countString.AppendInt(aCount);
  2568   const char16_t *strings[1] = { countString.get() };
  2569   if (aCount > 1) {
  2570     mBundle->FormatStringFromName(aCancelMessageMultiple, strings, 1,
  2571                                   getter_Copies(message));
  2572     mBundle->FormatStringFromName(MOZ_UTF16("cancelDownloadsOKTextMultiple"),
  2573                                   strings, 1, getter_Copies(quitButton));
  2574   } else {
  2575     mBundle->GetStringFromName(aCancelMessageSingle, getter_Copies(message));
  2576     mBundle->GetStringFromName(MOZ_UTF16("cancelDownloadsOKText"),
  2577                                getter_Copies(quitButton));
  2580   mBundle->GetStringFromName(aDontCancelButton, getter_Copies(dontQuitButton));
  2582   // Get Download Manager window, to be parent of alert.
  2583   nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
  2584   nsCOMPtr<nsIDOMWindow> dmWindow;
  2585   if (wm) {
  2586     wm->GetMostRecentWindow(MOZ_UTF16("Download:Manager"),
  2587                             getter_AddRefs(dmWindow));
  2590   // Show alert.
  2591   nsCOMPtr<nsIPromptService> prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
  2592   if (prompter) {
  2593     int32_t flags = (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_0) + (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1);
  2594     bool nothing = false;
  2595     int32_t button;
  2596     prompter->ConfirmEx(dmWindow, title, message, flags, quitButton.get(), dontQuitButton.get(), nullptr, nullptr, &nothing, &button);
  2598     aCancelDownloads->SetData(button == 1);
  2602 ////////////////////////////////////////////////////////////////////////////////
  2603 //// nsDownload
  2605 NS_IMPL_CLASSINFO(nsDownload, nullptr, 0, NS_DOWNLOAD_CID)
  2606 NS_IMPL_ISUPPORTS_CI(
  2607     nsDownload
  2608   , nsIDownload
  2609   , nsITransfer
  2610   , nsIWebProgressListener
  2611   , nsIWebProgressListener2
  2614 nsDownload::nsDownload() : mDownloadState(nsIDownloadManager::DOWNLOAD_NOTSTARTED),
  2615                            mID(0),
  2616                            mPercentComplete(0),
  2617                            mCurrBytes(0),
  2618                            mMaxBytes(-1),
  2619                            mStartTime(0),
  2620                            mLastUpdate(PR_Now() - (uint32_t)gUpdateInterval),
  2621                            mResumedAt(-1),
  2622                            mSpeed(0),
  2623                            mHasMultipleFiles(false),
  2624                            mPrivate(false),
  2625                            mAutoResume(DONT_RESUME)
  2629 nsDownload::~nsDownload()
  2633 NS_IMETHODIMP nsDownload::SetSha256Hash(const nsACString& aHash) {
  2634   MOZ_ASSERT(NS_IsMainThread(), "Must call SetSha256Hash on main thread");
  2635   // This will be used later to query the application reputation service.
  2636   mHash = aHash;
  2637   return NS_OK;
  2640 NS_IMETHODIMP nsDownload::SetSignatureInfo(nsIArray* aSignatureInfo) {
  2641   MOZ_ASSERT(NS_IsMainThread(), "Must call SetSignatureInfo on main thread");
  2642   // This will be used later to query the application reputation service.
  2643   mSignatureInfo = aSignatureInfo;
  2644   return NS_OK;
  2647 #ifdef MOZ_ENABLE_GIO
  2648 static void gio_set_metadata_done(GObject *source_obj, GAsyncResult *res, gpointer user_data)
  2650   GError *err = nullptr;
  2651   g_file_set_attributes_finish(G_FILE(source_obj), res, nullptr, &err);
  2652   if (err) {
  2653 #ifdef DEBUG
  2654     NS_DebugBreak(NS_DEBUG_WARNING, "Set file metadata failed: ", err->message, __FILE__, __LINE__);
  2655 #endif
  2656     g_error_free(err);
  2659 #endif
  2661 nsresult
  2662 nsDownload::SetState(DownloadState aState)
  2664   NS_ASSERTION(mDownloadState != aState,
  2665                "Trying to set the download state to what it already is set to!");
  2667   int16_t oldState = mDownloadState;
  2668   mDownloadState = aState;
  2670   // We don't want to lose access to our member variables
  2671   nsRefPtr<nsDownload> kungFuDeathGrip = this;
  2673   // When the state changed listener is dispatched, queries to the database and
  2674   // the download manager api should reflect what the nsIDownload object would
  2675   // return. So, if a download is done (finished, canceled, etc.), it should
  2676   // first be removed from the current downloads.  We will also have to update
  2677   // the database *before* notifying listeners.  At this point, you can safely
  2678   // dispatch to the observers as well.
  2679   switch (aState) {
  2680     case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
  2681     case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
  2682     case nsIDownloadManager::DOWNLOAD_DIRTY:
  2683     case nsIDownloadManager::DOWNLOAD_CANCELED:
  2684     case nsIDownloadManager::DOWNLOAD_FAILED:
  2685 #ifdef ANDROID
  2686       // If we still have a temp file, remove it
  2687       bool tempExists;
  2688       if (mTempFile && NS_SUCCEEDED(mTempFile->Exists(&tempExists)) && tempExists) {
  2689         nsresult rv = mTempFile->Remove(false);
  2690         NS_ENSURE_SUCCESS(rv, rv);
  2692 #endif
  2694       // Transfers are finished, so break the reference cycle
  2695       Finalize();
  2696       break;
  2697 #ifdef DOWNLOAD_SCANNER
  2698     case nsIDownloadManager::DOWNLOAD_SCANNING:
  2700       nsresult rv = mDownloadManager->mScanner ? mDownloadManager->mScanner->ScanDownload(this) : NS_ERROR_NOT_INITIALIZED;
  2701       // If we failed, then fall through to 'download finished'
  2702       if (NS_SUCCEEDED(rv))
  2703         break;
  2704       mDownloadState = aState = nsIDownloadManager::DOWNLOAD_FINISHED;
  2706 #endif
  2707     case nsIDownloadManager::DOWNLOAD_FINISHED:
  2709       nsresult rv = ExecuteDesiredAction();
  2710       if (NS_FAILED(rv)) {
  2711         // We've failed to execute the desired action.  As a result, we should
  2712         // fail the download so the user can try again.
  2713         (void)FailDownload(rv, nullptr);
  2714         return rv;
  2717       // Now that we're done with handling the download, clean it up
  2718       Finalize();
  2720       nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
  2722       // Master pref to control this function.
  2723       bool showTaskbarAlert = true;
  2724       if (pref)
  2725         pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert);
  2727       if (showTaskbarAlert) {
  2728         int32_t alertInterval = 2000;
  2729         if (pref)
  2730           pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
  2732         int64_t alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC;
  2733         int64_t goat = PR_Now() - mStartTime;
  2734         showTaskbarAlert = goat > alertIntervalUSec;
  2736         int32_t size = mPrivate ?
  2737             mDownloadManager->mCurrentPrivateDownloads.Count() :
  2738             mDownloadManager->mCurrentDownloads.Count();
  2739         if (showTaskbarAlert && size == 0) {
  2740           nsCOMPtr<nsIAlertsService> alerts =
  2741             do_GetService("@mozilla.org/alerts-service;1");
  2742           if (alerts) {
  2743               nsXPIDLString title, message;
  2745               mDownloadManager->mBundle->GetStringFromName(
  2746                   MOZ_UTF16("downloadsCompleteTitle"),
  2747                   getter_Copies(title));
  2748               mDownloadManager->mBundle->GetStringFromName(
  2749                   MOZ_UTF16("downloadsCompleteMsg"),
  2750                   getter_Copies(message));
  2752               bool removeWhenDone =
  2753                 mDownloadManager->GetRetentionBehavior() == 0;
  2755               // If downloads are automatically removed per the user's
  2756               // retention policy, there's no reason to make the text clickable
  2757               // because if it is, they'll click open the download manager and
  2758               // the items they downloaded will have been removed.
  2759               alerts->ShowAlertNotification(
  2760                   NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title,
  2761                   message, !removeWhenDone,
  2762                   mPrivate ? NS_LITERAL_STRING("private") : NS_LITERAL_STRING("non-private"),
  2763                   mDownloadManager, EmptyString(), NS_LITERAL_STRING("auto"),
  2764                   EmptyString(), nullptr);
  2769 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK)
  2770       nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget);
  2771       nsCOMPtr<nsIFile> file;
  2772       nsAutoString path;
  2774       if (fileURL &&
  2775           NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
  2776           file &&
  2777           NS_SUCCEEDED(file->GetPath(path))) {
  2779 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
  2780         // On Windows and Gtk, add the download to the system's "recent documents"
  2781         // list, with a pref to disable.
  2783           bool addToRecentDocs = true;
  2784           if (pref)
  2785             pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
  2787           if (addToRecentDocs && !mPrivate) {
  2788 #ifdef XP_WIN
  2789             ::SHAddToRecentDocs(SHARD_PATHW, path.get());
  2790 #elif defined(MOZ_WIDGET_GTK)
  2791             GtkRecentManager* manager = gtk_recent_manager_get_default();
  2793             gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(),
  2794                                            nullptr, nullptr);
  2795             if (uri) {
  2796               gtk_recent_manager_add_item(manager, uri);
  2797               g_free(uri);
  2799 #endif
  2801 #ifdef MOZ_ENABLE_GIO
  2802           // Use GIO to store the source URI for later display in the file manager.
  2803           GFile* gio_file = g_file_new_for_path(NS_ConvertUTF16toUTF8(path).get());
  2804           nsCString source_uri;
  2805           mSource->GetSpec(source_uri);
  2806           GFileInfo *file_info = g_file_info_new();
  2807           g_file_info_set_attribute_string(file_info, "metadata::download-uri", source_uri.get());
  2808           g_file_set_attributes_async(gio_file,
  2809                                       file_info,
  2810                                       G_FILE_QUERY_INFO_NONE,
  2811                                       G_PRIORITY_DEFAULT,
  2812                                       nullptr, gio_set_metadata_done, nullptr);
  2813           g_object_unref(file_info);
  2814           g_object_unref(gio_file);
  2815 #endif
  2817 #endif
  2818 #ifdef XP_MACOSX
  2819         // On OS X, make the downloads stack bounce.
  2820         CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault,
  2821                                                  NS_ConvertUTF16toUTF8(path).get(),
  2822                                                  kCFStringEncodingUTF8);
  2823         CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
  2824         ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
  2825                                                observedObject, nullptr, TRUE);
  2826         ::CFRelease(observedObject);
  2827 #endif
  2828 #ifdef MOZ_WIDGET_ANDROID
  2829         nsCOMPtr<nsIMIMEInfo> mimeInfo;
  2830         nsAutoCString contentType;
  2831         GetMIMEInfo(getter_AddRefs(mimeInfo));
  2833         if (mimeInfo)
  2834           mimeInfo->GetMIMEType(contentType);
  2836         mozilla::widget::android::GeckoAppShell::ScanMedia(path, NS_ConvertUTF8toUTF16(contentType));
  2837 #endif
  2840 #ifdef XP_WIN
  2841       // Adjust file attributes so that by default, new files are indexed
  2842       // by desktop search services. Skip off those that land in the temp
  2843       // folder.
  2844       nsCOMPtr<nsIFile> tempDir, fileDir;
  2845       rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempDir));
  2846       NS_ENSURE_SUCCESS(rv, rv);
  2847       (void)file->GetParent(getter_AddRefs(fileDir));
  2849       bool isTemp = false;
  2850       if (fileDir)
  2851         (void)fileDir->Equals(tempDir, &isTemp);
  2853       nsCOMPtr<nsILocalFileWin> localFileWin(do_QueryInterface(file));
  2854       if (!isTemp && localFileWin)
  2855         (void)localFileWin->SetFileAttributesWin(nsILocalFileWin::WFA_SEARCH_INDEXED);
  2856 #endif
  2858 #endif
  2859       // Now remove the download if the user's retention policy is "Remove when Done"
  2860       if (mDownloadManager->GetRetentionBehavior() == 0)
  2861         mDownloadManager->RemoveDownload(mGUID);
  2863     break;
  2864   default:
  2865     break;
  2868   // Before notifying the listener, we must update the database so that calls
  2869   // to it work out properly.
  2870   nsresult rv = UpdateDB();
  2871   NS_ENSURE_SUCCESS(rv, rv);
  2873   mDownloadManager->NotifyListenersOnDownloadStateChange(oldState, this);
  2875   switch (mDownloadState) {
  2876     case nsIDownloadManager::DOWNLOAD_DOWNLOADING:
  2877       // Only send the dl-start event to downloads that are actually starting.
  2878       if (oldState == nsIDownloadManager::DOWNLOAD_QUEUED) {
  2879         if (!mPrivate)
  2880           mDownloadManager->SendEvent(this, "dl-start");
  2882       break;
  2883     case nsIDownloadManager::DOWNLOAD_FAILED:
  2884       if (!mPrivate)
  2885         mDownloadManager->SendEvent(this, "dl-failed");
  2886       break;
  2887     case nsIDownloadManager::DOWNLOAD_SCANNING:
  2888       if (!mPrivate)
  2889         mDownloadManager->SendEvent(this, "dl-scanning");
  2890       break;
  2891     case nsIDownloadManager::DOWNLOAD_FINISHED:
  2892       if (!mPrivate)
  2893         mDownloadManager->SendEvent(this, "dl-done");
  2894       break;
  2895     case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
  2896     case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
  2897       if (!mPrivate)
  2898         mDownloadManager->SendEvent(this, "dl-blocked");
  2899       break;
  2900     case nsIDownloadManager::DOWNLOAD_DIRTY:
  2901       if (!mPrivate)
  2902         mDownloadManager->SendEvent(this, "dl-dirty");
  2903       break;
  2904     case nsIDownloadManager::DOWNLOAD_CANCELED:
  2905       if (!mPrivate)
  2906         mDownloadManager->SendEvent(this, "dl-cancel");
  2907       break;
  2908     default:
  2909       break;
  2911   return NS_OK;
  2914 ////////////////////////////////////////////////////////////////////////////////
  2915 //// nsIWebProgressListener2
  2917 NS_IMETHODIMP
  2918 nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
  2919                                nsIRequest *aRequest,
  2920                                int64_t aCurSelfProgress,
  2921                                int64_t aMaxSelfProgress,
  2922                                int64_t aCurTotalProgress,
  2923                                int64_t aMaxTotalProgress)
  2925   if (!mRequest)
  2926     mRequest = aRequest; // used for pause/resume
  2928   if (mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED) {
  2929     // Obtain the referrer
  2930     nsresult rv;
  2931     nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  2932     nsCOMPtr<nsIURI> referrer = mReferrer;
  2933     if (channel)
  2934       (void)NS_GetReferrerFromChannel(channel, getter_AddRefs(mReferrer));
  2936     // Restore the original referrer if the new one isn't useful
  2937     if (!mReferrer)
  2938       mReferrer = referrer;
  2940     // If we have a MIME info, we know that exthandler has already added this to
  2941     // the history, but if we do not, we'll have to add it ourselves.
  2942     if (!mMIMEInfo && !mPrivate) {
  2943       nsCOMPtr<nsIDownloadHistory> dh =
  2944         do_GetService(NS_DOWNLOADHISTORY_CONTRACTID);
  2945       if (dh)
  2946         (void)dh->AddDownload(mSource, mReferrer, mStartTime, mTarget);
  2949     // Fetch the entityID, but if we can't get it, don't panic (non-resumable)
  2950     nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(aRequest));
  2951     if (resumableChannel)
  2952       (void)resumableChannel->GetEntityID(mEntityID);
  2954     // Before we update the state and dispatch state notifications, we want to
  2955     // ensure that we have the correct state for this download with regards to
  2956     // its percent completion and size.
  2957     SetProgressBytes(0, aMaxTotalProgress);
  2959     // Update the state and the database
  2960     rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
  2961     NS_ENSURE_SUCCESS(rv, rv);
  2964   // filter notifications since they come in so frequently
  2965   PRTime now = PR_Now();
  2966   PRIntervalTime delta = now - mLastUpdate;
  2967   if (delta < gUpdateInterval)
  2968     return NS_OK;
  2970   mLastUpdate = now;
  2972   // Calculate the speed using the elapsed delta time and bytes downloaded
  2973   // during that time for more accuracy.
  2974   double elapsedSecs = double(delta) / PR_USEC_PER_SEC;
  2975   if (elapsedSecs > 0) {
  2976     double speed = double(aCurTotalProgress - mCurrBytes) / elapsedSecs;
  2977     if (mCurrBytes == 0) {
  2978       mSpeed = speed;
  2979     } else {
  2980       // Calculate 'smoothed average' of 10 readings.
  2981       mSpeed = mSpeed * 0.9 + speed * 0.1;
  2985   SetProgressBytes(aCurTotalProgress, aMaxTotalProgress);
  2987   // Report to the listener our real sizes
  2988   int64_t currBytes, maxBytes;
  2989   (void)GetAmountTransferred(&currBytes);
  2990   (void)GetSize(&maxBytes);
  2991   mDownloadManager->NotifyListenersOnProgressChange(
  2992     aWebProgress, aRequest, currBytes, maxBytes, currBytes, maxBytes, this);
  2994   // If the maximums are different, then there must be more than one file
  2995   if (aMaxSelfProgress != aMaxTotalProgress)
  2996     mHasMultipleFiles = true;
  2998   return NS_OK;
  3001 NS_IMETHODIMP
  3002 nsDownload::OnRefreshAttempted(nsIWebProgress *aWebProgress,
  3003                                nsIURI *aUri,
  3004                                int32_t aDelay,
  3005                                bool aSameUri,
  3006                                bool *allowRefresh)
  3008   *allowRefresh = true;
  3009   return NS_OK;
  3012 ////////////////////////////////////////////////////////////////////////////////
  3013 //// nsIWebProgressListener
  3015 NS_IMETHODIMP
  3016 nsDownload::OnProgressChange(nsIWebProgress *aWebProgress,
  3017                              nsIRequest *aRequest,
  3018                              int32_t aCurSelfProgress,
  3019                              int32_t aMaxSelfProgress,
  3020                              int32_t aCurTotalProgress,
  3021                              int32_t aMaxTotalProgress)
  3023   return OnProgressChange64(aWebProgress, aRequest,
  3024                             aCurSelfProgress, aMaxSelfProgress,
  3025                             aCurTotalProgress, aMaxTotalProgress);
  3028 NS_IMETHODIMP
  3029 nsDownload::OnLocationChange(nsIWebProgress *aWebProgress,
  3030                              nsIRequest *aRequest, nsIURI *aLocation,
  3031                              uint32_t aFlags)
  3033   return NS_OK;
  3036 NS_IMETHODIMP
  3037 nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
  3038                            nsIRequest *aRequest, nsresult aStatus,
  3039                            const char16_t *aMessage)
  3041   if (NS_FAILED(aStatus))
  3042     return FailDownload(aStatus, aMessage);
  3043   return NS_OK;
  3046 NS_IMETHODIMP
  3047 nsDownload::OnStateChange(nsIWebProgress *aWebProgress,
  3048                           nsIRequest *aRequest, uint32_t aStateFlags,
  3049                           nsresult aStatus)
  3051   MOZ_ASSERT(NS_IsMainThread(), "Must call OnStateChange in main thread");
  3053   // We don't want to lose access to our member variables
  3054   nsRefPtr<nsDownload> kungFuDeathGrip = this;
  3056   // Check if we're starting a request; the NETWORK flag is necessary to not
  3057   // pick up the START of *each* file but only for the whole request
  3058   if ((aStateFlags & STATE_START) && (aStateFlags & STATE_IS_NETWORK)) {
  3059     nsresult rv;
  3060     nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
  3061     if (NS_SUCCEEDED(rv)) {
  3062       uint32_t status;
  3063       rv = channel->GetResponseStatus(&status);
  3064       // HTTP 450 - Blocked by parental control proxies
  3065       if (NS_SUCCEEDED(rv) && status == 450) {
  3066         // Cancel using the provided object
  3067         (void)Cancel();
  3069         // Fail the download
  3070         (void)SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL);
  3073   } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK) &&
  3074              IsFinishable()) {
  3075     // We got both STOP and NETWORK so that means the whole request is done
  3076     // (and not just a single file if there are multiple files)
  3077     if (NS_SUCCEEDED(aStatus)) {
  3078       // We can't completely trust the bytes we've added up because we might be
  3079       // missing on some/all of the progress updates (especially from cache).
  3080       // Our best bet is the file itself, but if for some reason it's gone or
  3081       // if we have multiple files, the next best is what we've calculated.
  3082       int64_t fileSize;
  3083       nsCOMPtr<nsIFile> file;
  3084       //  We need a nsIFile clone to deal with file size caching issues. :(
  3085       nsCOMPtr<nsIFile> clone;
  3086       if (!mHasMultipleFiles &&
  3087           NS_SUCCEEDED(GetTargetFile(getter_AddRefs(file))) &&
  3088           NS_SUCCEEDED(file->Clone(getter_AddRefs(clone))) &&
  3089           NS_SUCCEEDED(clone->GetFileSize(&fileSize)) && fileSize > 0) {
  3090         mCurrBytes = mMaxBytes = fileSize;
  3092         // If we resumed, keep the fact that we did and fix size calculations
  3093         if (WasResumed())
  3094           mResumedAt = 0;
  3095       } else if (mMaxBytes == -1) {
  3096         mMaxBytes = mCurrBytes;
  3097       } else {
  3098         mCurrBytes = mMaxBytes;
  3101       mPercentComplete = 100;
  3102       mLastUpdate = PR_Now();
  3104 #ifdef DOWNLOAD_SCANNER
  3105       bool scan = true;
  3106       nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
  3107       if (prefs)
  3108         (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan);
  3110       if (scan)
  3111         (void)SetState(nsIDownloadManager::DOWNLOAD_SCANNING);
  3112       else
  3113         (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
  3114 #else
  3115       (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
  3116 #endif
  3117     } else {
  3118       // We failed for some unknown reason -- fail with a generic message
  3119       (void)FailDownload(aStatus, nullptr);
  3123   mDownloadManager->NotifyListenersOnStateChange(aWebProgress, aRequest,
  3124                                                  aStateFlags, aStatus, this);
  3125   return NS_OK;
  3128 NS_IMETHODIMP
  3129 nsDownload::OnSecurityChange(nsIWebProgress *aWebProgress,
  3130                              nsIRequest *aRequest, uint32_t aState)
  3132   return NS_OK;
  3135 ////////////////////////////////////////////////////////////////////////////////
  3136 //// nsIDownload
  3138 NS_IMETHODIMP
  3139 nsDownload::Init(nsIURI *aSource,
  3140                  nsIURI *aTarget,
  3141                  const nsAString& aDisplayName,
  3142                  nsIMIMEInfo *aMIMEInfo,
  3143                  PRTime aStartTime,
  3144                  nsIFile *aTempFile,
  3145                  nsICancelable *aCancelable,
  3146                  bool aIsPrivate)
  3148   NS_WARNING("Huh...how did we get here?!");
  3149   return NS_OK;
  3152 NS_IMETHODIMP
  3153 nsDownload::GetState(int16_t *aState)
  3155   *aState = mDownloadState;
  3156   return NS_OK;
  3159 NS_IMETHODIMP
  3160 nsDownload::GetDisplayName(nsAString &aDisplayName)
  3162   aDisplayName = mDisplayName;
  3163   return NS_OK;
  3166 NS_IMETHODIMP
  3167 nsDownload::GetCancelable(nsICancelable **aCancelable)
  3169   *aCancelable = mCancelable;
  3170   NS_IF_ADDREF(*aCancelable);
  3171   return NS_OK;
  3174 NS_IMETHODIMP
  3175 nsDownload::GetTarget(nsIURI **aTarget)
  3177   *aTarget = mTarget;
  3178   NS_IF_ADDREF(*aTarget);
  3179   return NS_OK;
  3182 NS_IMETHODIMP
  3183 nsDownload::GetSource(nsIURI **aSource)
  3185   *aSource = mSource;
  3186   NS_IF_ADDREF(*aSource);
  3187   return NS_OK;
  3190 NS_IMETHODIMP
  3191 nsDownload::GetStartTime(int64_t *aStartTime)
  3193   *aStartTime = mStartTime;
  3194   return NS_OK;
  3197 NS_IMETHODIMP
  3198 nsDownload::GetPercentComplete(int32_t *aPercentComplete)
  3200   *aPercentComplete = mPercentComplete;
  3201   return NS_OK;
  3204 NS_IMETHODIMP
  3205 nsDownload::GetAmountTransferred(int64_t *aAmountTransferred)
  3207   *aAmountTransferred = mCurrBytes + (WasResumed() ? mResumedAt : 0);
  3208   return NS_OK;
  3211 NS_IMETHODIMP
  3212 nsDownload::GetSize(int64_t *aSize)
  3214   *aSize = mMaxBytes + (WasResumed() && mMaxBytes != -1 ? mResumedAt : 0);
  3215   return NS_OK;
  3218 NS_IMETHODIMP
  3219 nsDownload::GetMIMEInfo(nsIMIMEInfo **aMIMEInfo)
  3221   *aMIMEInfo = mMIMEInfo;
  3222   NS_IF_ADDREF(*aMIMEInfo);
  3223   return NS_OK;
  3226 NS_IMETHODIMP
  3227 nsDownload::GetTargetFile(nsIFile **aTargetFile)
  3229   nsresult rv;
  3231   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
  3232   if (NS_FAILED(rv)) {
  3233     return rv;
  3236   nsCOMPtr<nsIFile> file;
  3237   rv = fileURL->GetFile(getter_AddRefs(file));
  3238   if (NS_FAILED(rv)) {
  3239     return rv;
  3242   file.forget(aTargetFile);
  3243   return rv;
  3246 NS_IMETHODIMP
  3247 nsDownload::GetSpeed(double *aSpeed)
  3249   *aSpeed = mSpeed;
  3250   return NS_OK;
  3253 NS_IMETHODIMP
  3254 nsDownload::GetId(uint32_t *aId)
  3256   if (mPrivate) {
  3257     return NS_ERROR_NOT_AVAILABLE;
  3259   *aId = mID;
  3260   return NS_OK;
  3263 NS_IMETHODIMP
  3264 nsDownload::GetGuid(nsACString &aGUID)
  3266   aGUID = mGUID;
  3267   return NS_OK;
  3270 NS_IMETHODIMP
  3271 nsDownload::GetReferrer(nsIURI **referrer)
  3273   NS_IF_ADDREF(*referrer = mReferrer);
  3274   return NS_OK;
  3277 NS_IMETHODIMP
  3278 nsDownload::GetResumable(bool *resumable)
  3280   *resumable = IsResumable();
  3281   return NS_OK;
  3284 NS_IMETHODIMP
  3285 nsDownload::GetIsPrivate(bool *isPrivate)
  3287   *isPrivate = mPrivate;
  3288   return NS_OK;
  3291 ////////////////////////////////////////////////////////////////////////////////
  3292 //// nsDownload Helper Functions
  3294 void
  3295 nsDownload::Finalize()
  3297   // We're stopping, so break the cycle we created at download start
  3298   mCancelable = nullptr;
  3300   // Reset values that aren't needed anymore, so the DB can be updated as well
  3301   mEntityID.Truncate();
  3302   mTempFile = nullptr;
  3304   // Remove ourself from the active downloads
  3305   nsCOMArray<nsDownload>& currentDownloads = mPrivate ?
  3306     mDownloadManager->mCurrentPrivateDownloads :
  3307     mDownloadManager->mCurrentDownloads;
  3308   (void)currentDownloads.RemoveObject(this);
  3310   // Make sure we do not automatically resume
  3311   mAutoResume = DONT_RESUME;
  3314 nsresult
  3315 nsDownload::ExecuteDesiredAction()
  3317   // nsExternalHelperAppHandler is the only caller of AddDownload that sets a
  3318   // tempfile parameter. In this case, execute the desired action according to
  3319   // the saved mime info.
  3320   if (!mTempFile) {
  3321     return NS_OK;
  3324   // We need to bail if for some reason the temp file got removed
  3325   bool fileExists;
  3326   if (NS_FAILED(mTempFile->Exists(&fileExists)) || !fileExists)
  3327     return NS_ERROR_FILE_NOT_FOUND;
  3329   // Assume an unknown action is save to disk
  3330   nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
  3331   if (mMIMEInfo) {
  3332     nsresult rv = mMIMEInfo->GetPreferredAction(&action);
  3333     NS_ENSURE_SUCCESS(rv, rv);
  3336   nsresult retVal = NS_OK;
  3337   switch (action) {
  3338     case nsIMIMEInfo::saveToDisk:
  3339       // Move the file to the proper location
  3340       retVal = MoveTempToTarget();
  3341       break;
  3342     case nsIMIMEInfo::useHelperApp:
  3343     case nsIMIMEInfo::useSystemDefault:
  3344       // For these cases we have to move the file to the target location and
  3345       // open with the appropriate application
  3346       retVal = OpenWithApplication();
  3347       break;
  3348     default:
  3349       break;
  3352   return retVal;
  3355 nsresult
  3356 nsDownload::MoveTempToTarget()
  3358   nsCOMPtr<nsIFile> target;
  3359   nsresult rv = GetTargetFile(getter_AddRefs(target));
  3360   NS_ENSURE_SUCCESS(rv, rv);
  3362   // MoveTo will fail if the file already exists, but we've already obtained
  3363   // confirmation from the user that this is OK, so remove it if it exists.
  3364   bool fileExists;
  3365   if (NS_SUCCEEDED(target->Exists(&fileExists)) && fileExists) {
  3366     rv = target->Remove(false);
  3367     NS_ENSURE_SUCCESS(rv, rv);
  3370   // Extract the new leaf name from the file location
  3371   nsAutoString fileName;
  3372   rv = target->GetLeafName(fileName);
  3373   NS_ENSURE_SUCCESS(rv, rv);
  3374   nsCOMPtr<nsIFile> dir;
  3375   rv = target->GetParent(getter_AddRefs(dir));
  3376   NS_ENSURE_SUCCESS(rv, rv);
  3377   rv = mTempFile->MoveTo(dir, fileName);
  3378   NS_ENSURE_SUCCESS(rv, rv);
  3380   return NS_OK;
  3383 nsresult
  3384 nsDownload::OpenWithApplication()
  3386   // First move the temporary file to the target location
  3387   nsCOMPtr<nsIFile> target;
  3388   nsresult rv = GetTargetFile(getter_AddRefs(target));
  3389   NS_ENSURE_SUCCESS(rv, rv);
  3391   // Move the temporary file to the target location
  3392   rv = MoveTempToTarget();
  3393   NS_ENSURE_SUCCESS(rv, rv);
  3395   // We do not verify the return value here because, irrespective of success
  3396   // or failure of the method, the deletion of temp file has to take place, as
  3397   // per the corresponding preference. But we store this separately as this is
  3398   // what we ultimately return from this function.
  3399   nsresult retVal = mMIMEInfo->LaunchWithFile(target);
  3401   bool deleteTempFileOnExit;
  3402   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
  3403   if (!prefs || NS_FAILED(prefs->GetBoolPref(PREF_BH_DELETETEMPFILEONEXIT,
  3404                                              &deleteTempFileOnExit))) {
  3405     // No prefservice or no pref set; use default value
  3406 #if !defined(XP_MACOSX)
  3407     // Mac users have been very verbal about temp files being deleted on
  3408     // app exit - they don't like it - but we'll continue to do this on
  3409     // other platforms for now.
  3410     deleteTempFileOnExit = true;
  3411 #else
  3412     deleteTempFileOnExit = false;
  3413 #endif
  3416   // Always schedule files to be deleted at the end of the private browsing
  3417   // mode, regardless of the value of the pref.
  3418   if (deleteTempFileOnExit || mPrivate) {
  3419     // Use the ExternalHelperAppService to push the temporary file to the list
  3420     // of files to be deleted on exit.
  3421     nsCOMPtr<nsPIExternalAppLauncher> appLauncher(do_GetService
  3422                     (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID));
  3424     // Even if we are unable to get this service we return the result
  3425     // of LaunchWithFile() which makes more sense.
  3426     if (appLauncher) {
  3427       if (mPrivate) {
  3428         (void)appLauncher->DeleteTemporaryPrivateFileWhenPossible(target);
  3429       } else {
  3430         (void)appLauncher->DeleteTemporaryFileOnExit(target);
  3435   return retVal;
  3438 void
  3439 nsDownload::SetStartTime(int64_t aStartTime)
  3441   mStartTime = aStartTime;
  3442   mLastUpdate = aStartTime;
  3445 void
  3446 nsDownload::SetProgressBytes(int64_t aCurrBytes, int64_t aMaxBytes)
  3448   mCurrBytes = aCurrBytes;
  3449   mMaxBytes = aMaxBytes;
  3451   // Get the real bytes that include resume position
  3452   int64_t currBytes, maxBytes;
  3453   (void)GetAmountTransferred(&currBytes);
  3454   (void)GetSize(&maxBytes);
  3456   if (currBytes == maxBytes)
  3457     mPercentComplete = 100;
  3458   else if (maxBytes <= 0)
  3459     mPercentComplete = -1;
  3460   else
  3461     mPercentComplete = (int32_t)((double)currBytes / maxBytes * 100 + .5);
  3464 NS_IMETHODIMP
  3465 nsDownload::Pause()
  3467   if (!IsResumable())
  3468     return NS_ERROR_UNEXPECTED;
  3470   nsresult rv = CancelTransfer();
  3471   NS_ENSURE_SUCCESS(rv, rv);
  3473   return SetState(nsIDownloadManager::DOWNLOAD_PAUSED);
  3476 nsresult
  3477 nsDownload::CancelTransfer()
  3479   nsresult rv = NS_OK;
  3480   if (mCancelable) {
  3481     rv = mCancelable->Cancel(NS_BINDING_ABORTED);
  3482     // we're done with this, so break the cycle
  3483     mCancelable = nullptr;
  3486   return rv;
  3489 NS_IMETHODIMP
  3490 nsDownload::Cancel()
  3492   // Don't cancel if download is already finished
  3493   if (IsFinished())
  3494     return NS_OK;
  3496   // Have the download cancel its connection
  3497   (void)CancelTransfer();
  3499   // Dump the temp file because we know we don't need the file anymore. The
  3500   // underlying transfer creating the file doesn't delete the file because it
  3501   // can't distinguish between a pause that cancels the transfer or a real
  3502   // cancel.
  3503   if (mTempFile) {
  3504     bool exists;
  3505     mTempFile->Exists(&exists);
  3506     if (exists)
  3507       mTempFile->Remove(false);
  3510   nsCOMPtr<nsIFile> file;
  3511   if (NS_SUCCEEDED(GetTargetFile(getter_AddRefs(file))))
  3513     bool exists;
  3514     file->Exists(&exists);
  3515     if (exists)
  3516       file->Remove(false);
  3519   nsresult rv = SetState(nsIDownloadManager::DOWNLOAD_CANCELED);
  3520   NS_ENSURE_SUCCESS(rv, rv);
  3522   return NS_OK;
  3525 NS_IMETHODIMP
  3526 nsDownload::Resume()
  3528   if (!IsPaused() || !IsResumable())
  3529     return NS_ERROR_UNEXPECTED;
  3531   nsresult rv;
  3532   nsCOMPtr<nsIWebBrowserPersist> wbp =
  3533     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
  3534   NS_ENSURE_SUCCESS(rv, rv);
  3536   rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE |
  3537                             nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
  3538   NS_ENSURE_SUCCESS(rv, rv);
  3540   // Create a new channel for the source URI
  3541   nsCOMPtr<nsIChannel> channel;
  3542   nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(wbp));
  3543   rv = NS_NewChannel(getter_AddRefs(channel), mSource, nullptr, nullptr, ir);
  3544   NS_ENSURE_SUCCESS(rv, rv);
  3546   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
  3547   if (pbChannel) {
  3548     pbChannel->SetPrivate(mPrivate);
  3551   // Make sure we can get a file, either the temporary or the real target, for
  3552   // both purposes of file size and a target to write to
  3553   nsCOMPtr<nsIFile> targetLocalFile(mTempFile);
  3554   if (!targetLocalFile) {
  3555     rv = GetTargetFile(getter_AddRefs(targetLocalFile));
  3556     NS_ENSURE_SUCCESS(rv, rv);
  3559   // Get the file size to be used as an offset, but if anything goes wrong
  3560   // along the way, we'll silently restart at 0.
  3561   int64_t fileSize;
  3562   //  We need a nsIFile clone to deal with file size caching issues. :(
  3563   nsCOMPtr<nsIFile> clone;
  3564   if (NS_FAILED(targetLocalFile->Clone(getter_AddRefs(clone))) ||
  3565       NS_FAILED(clone->GetFileSize(&fileSize)))
  3566     fileSize = 0;
  3568   // Set the channel to resume at the right position along with the entityID
  3569   nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(channel));
  3570   if (!resumableChannel)
  3571     return NS_ERROR_UNEXPECTED;
  3572   rv = resumableChannel->ResumeAt(fileSize, mEntityID);
  3573   NS_ENSURE_SUCCESS(rv, rv);
  3575   // If we know the max size, we know what it should be when resuming
  3576   int64_t maxBytes;
  3577   GetSize(&maxBytes);
  3578   SetProgressBytes(0, maxBytes != -1 ? maxBytes - fileSize : -1);
  3579   // Track where we resumed because progress notifications restart at 0
  3580   mResumedAt = fileSize;
  3582   // Set the referrer
  3583   if (mReferrer) {
  3584     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
  3585     if (httpChannel) {
  3586       rv = httpChannel->SetReferrer(mReferrer);
  3587       NS_ENSURE_SUCCESS(rv, rv);
  3591   // Creates a cycle that will be broken when the download finishes
  3592   mCancelable = wbp;
  3593   (void)wbp->SetProgressListener(this);
  3595   // Save the channel using nsIWBP
  3596   rv = wbp->SaveChannel(channel, targetLocalFile);
  3597   if (NS_FAILED(rv)) {
  3598     mCancelable = nullptr;
  3599     (void)wbp->SetProgressListener(nullptr);
  3600     return rv;
  3603   return SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
  3606 NS_IMETHODIMP
  3607 nsDownload::Remove()
  3609   return mDownloadManager->RemoveDownload(mGUID);
  3612 NS_IMETHODIMP
  3613 nsDownload::Retry()
  3615   return mDownloadManager->RetryDownload(mGUID);
  3618 bool
  3619 nsDownload::IsPaused()
  3621   return mDownloadState == nsIDownloadManager::DOWNLOAD_PAUSED;
  3624 bool
  3625 nsDownload::IsResumable()
  3627   return !mEntityID.IsEmpty();
  3630 bool
  3631 nsDownload::WasResumed()
  3633   return mResumedAt != -1;
  3636 bool
  3637 nsDownload::ShouldAutoResume()
  3639   return mAutoResume == AUTO_RESUME;
  3642 bool
  3643 nsDownload::IsFinishable()
  3645   return mDownloadState == nsIDownloadManager::DOWNLOAD_NOTSTARTED ||
  3646          mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED ||
  3647          mDownloadState == nsIDownloadManager::DOWNLOAD_DOWNLOADING;
  3650 bool
  3651 nsDownload::IsFinished()
  3653   return mDownloadState == nsIDownloadManager::DOWNLOAD_FINISHED;
  3656 nsresult
  3657 nsDownload::UpdateDB()
  3659   NS_ASSERTION(mID, "Download ID is stored as zero.  This is bad!");
  3660   NS_ASSERTION(mDownloadManager, "Egads!  We have no download manager!");
  3662   mozIStorageStatement *stmt = mPrivate ?
  3663     mDownloadManager->mUpdatePrivateDownloadStatement : mDownloadManager->mUpdateDownloadStatement;
  3665   nsAutoString tempPath;
  3666   if (mTempFile)
  3667     (void)mTempFile->GetPath(tempPath);
  3668   nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), tempPath);
  3670   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), mStartTime);
  3671   NS_ENSURE_SUCCESS(rv, rv);
  3673   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), mLastUpdate);
  3674   NS_ENSURE_SUCCESS(rv, rv);
  3676   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), mDownloadState);
  3677   NS_ENSURE_SUCCESS(rv, rv);
  3679   if (mReferrer) {
  3680     nsAutoCString referrer;
  3681     rv = mReferrer->GetSpec(referrer);
  3682     NS_ENSURE_SUCCESS(rv, rv);
  3683     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("referrer"), referrer);
  3684   } else {
  3685     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("referrer"));
  3687   NS_ENSURE_SUCCESS(rv, rv);
  3689   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("entityID"), mEntityID);
  3690   NS_ENSURE_SUCCESS(rv, rv);
  3692   int64_t currBytes;
  3693   (void)GetAmountTransferred(&currBytes);
  3694   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("currBytes"), currBytes);
  3695   NS_ENSURE_SUCCESS(rv, rv);
  3697   int64_t maxBytes;
  3698   (void)GetSize(&maxBytes);
  3699   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("maxBytes"), maxBytes);
  3700   NS_ENSURE_SUCCESS(rv, rv);
  3702   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), mAutoResume);
  3703   NS_ENSURE_SUCCESS(rv, rv);
  3705   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mID);
  3706   NS_ENSURE_SUCCESS(rv, rv);
  3708   return stmt->Execute();
  3711 nsresult
  3712 nsDownload::FailDownload(nsresult aStatus, const char16_t *aMessage)
  3714   // Grab the bundle before potentially losing our member variables
  3715   nsCOMPtr<nsIStringBundle> bundle = mDownloadManager->mBundle;
  3717   (void)SetState(nsIDownloadManager::DOWNLOAD_FAILED);
  3719   // Get title for alert.
  3720   nsXPIDLString title;
  3721   nsresult rv = bundle->GetStringFromName(
  3722     MOZ_UTF16("downloadErrorAlertTitle"), getter_Copies(title));
  3723   NS_ENSURE_SUCCESS(rv, rv);
  3725   // Get a generic message if we weren't supplied one
  3726   nsXPIDLString message;
  3727   message = aMessage;
  3728   if (message.IsEmpty()) {
  3729     rv = bundle->GetStringFromName(
  3730       MOZ_UTF16("downloadErrorGeneric"), getter_Copies(message));
  3731     NS_ENSURE_SUCCESS(rv, rv);
  3734   // Get Download Manager window to be parent of alert
  3735   nsCOMPtr<nsIWindowMediator> wm =
  3736     do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
  3737   NS_ENSURE_SUCCESS(rv, rv);
  3738   nsCOMPtr<nsIDOMWindow> dmWindow;
  3739   rv = wm->GetMostRecentWindow(MOZ_UTF16("Download:Manager"),
  3740                                getter_AddRefs(dmWindow));
  3741   NS_ENSURE_SUCCESS(rv, rv);
  3743   // Show alert
  3744   nsCOMPtr<nsIPromptService> prompter =
  3745     do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv);
  3746   NS_ENSURE_SUCCESS(rv, rv);
  3747   return prompter->Alert(dmWindow, title, message);

mercurial