LCOV - code coverage report
Current view: directory - toolkit/components/downloads - nsDownloadManager.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1320 1040 78.8 %
Date: 2012-06-02 Functions: 110 94 85.5 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Blake Ross <blaker@netscape.com> (Original Author)
      24                 :  *   Ben Goodger <ben@netscape.com> (Original Author)
      25                 :  *   Shawn Wilsher <me@shawnwilsher.com>
      26                 :  *   Srirang G Doddihal <brahmana@doddihal.com>
      27                 :  *   Edward Lee <edward.lee@engineering.uiuc.edu>
      28                 :  *   Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>
      29                 :  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
      30                 :  *
      31                 :  * Alternatively, the contents of this file may be used under the terms of
      32                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      33                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      34                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      35                 :  * of those above. If you wish to allow use of your version of this file only
      36                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      37                 :  * use your version of this file under the terms of the MPL, indicate your
      38                 :  * decision by deleting the provisions above and replace them with the notice
      39                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      40                 :  * the provisions above, a recipient may use your version of this file under
      41                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      42                 :  *
      43                 :  * ***** END LICENSE BLOCK ***** */
      44                 : 
      45                 : #include "mozilla/Util.h"
      46                 : 
      47                 : #include "mozIStorageService.h"
      48                 : #include "nsIAlertsService.h"
      49                 : #include "nsIClassInfoImpl.h"
      50                 : #include "nsIDOMWindow.h"
      51                 : #include "nsIDownloadHistory.h"
      52                 : #include "nsIDownloadManagerUI.h"
      53                 : #include "nsIMIMEService.h"
      54                 : #include "nsIParentalControlsService.h"
      55                 : #include "nsIPrefService.h"
      56                 : #include "nsIPrivateBrowsingService.h"
      57                 : #include "nsIPromptService.h"
      58                 : #include "nsIResumableChannel.h"
      59                 : #include "nsIWebBrowserPersist.h"
      60                 : #include "nsIWindowMediator.h"
      61                 : #include "nsILocalFileWin.h"
      62                 : 
      63                 : #include "nsAppDirectoryServiceDefs.h"
      64                 : #include "nsArrayEnumerator.h"
      65                 : #include "nsCExternalHandlerService.h"
      66                 : #include "nsDirectoryServiceDefs.h"
      67                 : #include "nsDownloadManager.h"
      68                 : #include "nsNetUtil.h"
      69                 : 
      70                 : #include "mozStorageCID.h"
      71                 : #include "nsDocShellCID.h"
      72                 : #include "nsEmbedCID.h"
      73                 : #include "nsToolkitCompsCID.h"
      74                 : 
      75                 : #ifdef XP_WIN
      76                 : #include <shlobj.h>
      77                 : #ifdef DOWNLOAD_SCANNER
      78                 : #include "nsDownloadScanner.h"
      79                 : #endif
      80                 : #endif
      81                 : 
      82                 : #ifdef XP_MACOSX
      83                 : #include <CoreFoundation/CoreFoundation.h>
      84                 : #endif
      85                 : 
      86                 : #ifdef MOZ_WIDGET_ANDROID
      87                 : #include "AndroidBridge.h"
      88                 : #endif
      89                 : 
      90                 : using namespace mozilla;
      91                 : 
      92                 : #define DOWNLOAD_MANAGER_BUNDLE "chrome://mozapps/locale/downloads/downloads.properties"
      93                 : #define DOWNLOAD_MANAGER_ALERT_ICON "chrome://mozapps/skin/downloads/downloadIcon.png"
      94                 : #define PREF_BDM_SHOWALERTONCOMPLETE "browser.download.manager.showAlertOnComplete"
      95                 : #define PREF_BDM_SHOWALERTINTERVAL "browser.download.manager.showAlertInterval"
      96                 : #define PREF_BDM_RETENTION "browser.download.manager.retention"
      97                 : #define PREF_BDM_QUITBEHAVIOR "browser.download.manager.quitBehavior"
      98                 : #define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
      99                 : #define PREF_BDM_SCANWHENDONE "browser.download.manager.scanWhenDone"
     100                 : #define PREF_BDM_RESUMEONWAKEDELAY "browser.download.manager.resumeOnWakeDelay"
     101                 : #define PREF_BH_DELETETEMPFILEONEXIT "browser.helperApps.deleteTempFileOnExit"
     102                 : 
     103                 : static const PRInt64 gUpdateInterval = 400 * PR_USEC_PER_MSEC;
     104                 : 
     105                 : #define DM_SCHEMA_VERSION      8
     106                 : #define DM_DB_NAME             NS_LITERAL_STRING("downloads.sqlite")
     107                 : #define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt")
     108                 : 
     109                 : #define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
     110                 : 
     111                 : ////////////////////////////////////////////////////////////////////////////////
     112                 : //// nsDownloadManager
     113                 : 
     114            4896 : NS_IMPL_ISUPPORTS4(
     115                 :   nsDownloadManager
     116                 : , nsIDownloadManager
     117                 : , nsINavHistoryObserver
     118                 : , nsIObserver
     119                 : , nsISupportsWeakReference
     120                 : )
     121                 : 
     122                 : nsDownloadManager *nsDownloadManager::gDownloadManagerService = nsnull;
     123                 : 
     124                 : nsDownloadManager *
     125              35 : nsDownloadManager::GetSingleton()
     126                 : {
     127              35 :   if (gDownloadManagerService) {
     128               1 :     NS_ADDREF(gDownloadManagerService);
     129               1 :     return gDownloadManagerService;
     130                 :   }
     131                 : 
     132              34 :   gDownloadManagerService = new nsDownloadManager();
     133              34 :   if (gDownloadManagerService) {
     134              34 :     NS_ADDREF(gDownloadManagerService);
     135              34 :     if (NS_FAILED(gDownloadManagerService->Init()))
     136               0 :       NS_RELEASE(gDownloadManagerService);
     137                 :   }
     138                 : 
     139              34 :   return gDownloadManagerService;
     140                 : }
     141                 : 
     142              81 : nsDownloadManager::~nsDownloadManager()
     143                 : {
     144                 : #ifdef DOWNLOAD_SCANNER
     145                 :   if (mScanner) {
     146                 :     delete mScanner;
     147                 :     mScanner = nsnull;
     148                 :   }
     149                 : #endif
     150              27 :   gDownloadManagerService = nsnull;
     151             108 : }
     152                 : 
     153                 : nsresult
     154               3 : nsDownloadManager::ResumeRetry(nsDownload *aDl)
     155                 : {
     156                 :   // Keep a reference in case we need to cancel the download
     157               6 :   nsRefPtr<nsDownload> dl = aDl;
     158                 : 
     159                 :   // Try to resume the active download
     160               3 :   nsresult rv = dl->Resume();
     161                 : 
     162                 :   // If not, try to retry the download
     163               3 :   if (NS_FAILED(rv)) {
     164                 :     // First cancel the download so it's no longer active
     165               0 :     rv = CancelDownload(dl->mID);
     166                 : 
     167                 :     // Then retry it
     168               0 :     if (NS_SUCCEEDED(rv))
     169               0 :       rv = RetryDownload(dl->mID);
     170                 :   }
     171                 : 
     172               3 :   return rv;
     173                 : }
     174                 : 
     175                 : nsresult
     176              87 : nsDownloadManager::PauseAllDownloads(bool aSetResume)
     177                 : {
     178              87 :   nsresult retVal = NS_OK;
     179              96 :   for (PRInt32 i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
     180              18 :     nsRefPtr<nsDownload> dl = mCurrentDownloads[i];
     181                 : 
     182                 :     // Only pause things that need to be paused
     183               9 :     if (!dl->IsPaused()) {
     184                 :       // Set auto-resume before pausing so that it gets into the DB
     185               6 :       dl->mAutoResume = aSetResume ? nsDownload::AUTO_RESUME :
     186               6 :                                      nsDownload::DONT_RESUME;
     187                 : 
     188                 :       // Try to pause the download but don't bail now if we fail
     189               6 :       nsresult rv = dl->Pause();
     190               6 :       if (NS_FAILED(rv))
     191               2 :         retVal = rv;
     192                 :     }
     193                 :   }
     194                 : 
     195              87 :   return retVal;
     196                 : }
     197                 : 
     198                 : nsresult
     199              53 : nsDownloadManager::ResumeAllDownloads(bool aResumeAll)
     200                 : {
     201              53 :   nsresult retVal = NS_OK;
     202              63 :   for (PRInt32 i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
     203              20 :     nsRefPtr<nsDownload> dl = mCurrentDownloads[i];
     204                 : 
     205                 :     // If aResumeAll is true, then resume everything; otherwise, check if the
     206                 :     // download should auto-resume
     207              10 :     if (aResumeAll || dl->ShouldAutoResume()) {
     208                 :       // Reset auto-resume before retrying so that it gets into the DB through
     209                 :       // ResumeRetry's eventual call to SetState. We clear the value now so we
     210                 :       // don't accidentally query completed downloads that were previously
     211                 :       // auto-resumed (and try to resume them).
     212               3 :       dl->mAutoResume = nsDownload::DONT_RESUME;
     213                 : 
     214                 :       // Try to resume/retry the download but don't bail now if we fail
     215               3 :       nsresult rv = ResumeRetry(dl);
     216               3 :       if (NS_FAILED(rv))
     217               0 :         retVal = rv;
     218                 :     }
     219                 :   }
     220                 : 
     221              53 :   return retVal;
     222                 : }
     223                 : 
     224                 : nsresult
     225              46 : nsDownloadManager::RemoveAllDownloads()
     226                 : {
     227              46 :   nsresult rv = NS_OK;
     228              51 :   for (PRInt32 i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
     229              10 :     nsRefPtr<nsDownload> dl = mCurrentDownloads[0];
     230                 : 
     231                 :     nsresult result;
     232               5 :     if (dl->IsPaused() && GetQuitBehavior() != QUIT_AND_CANCEL)
     233               4 :       result = mCurrentDownloads.RemoveObject(dl);
     234                 :     else
     235               1 :       result = CancelDownload(dl->mID);
     236                 : 
     237                 :     // Track the failure, but don't miss out on other downloads
     238               5 :     if (NS_FAILED(result))
     239               0 :       rv = result;
     240                 :   }
     241                 : 
     242              46 :   return rv;
     243                 : }
     244                 : 
     245                 : nsresult
     246               3 : nsDownloadManager::RemoveDownloadsForURI(nsIURI *aURI)
     247                 : {
     248               6 :   mozStorageStatementScoper scope(mGetIdsForURIStatement);
     249                 : 
     250               6 :   nsCAutoString source;
     251               3 :   nsresult rv = aURI->GetSpec(source);
     252               3 :   NS_ENSURE_SUCCESS(rv, rv);
     253                 : 
     254               3 :   rv = mGetIdsForURIStatement->BindUTF8StringByName(
     255               3 :     NS_LITERAL_CSTRING("source"), source);
     256               3 :   NS_ENSURE_SUCCESS(rv, rv);
     257                 : 
     258               3 :   bool hasMore = false;
     259               6 :   nsAutoTArray<PRInt64, 4> downloads;
     260                 :   // Get all the downloads that match the provided URI
     261               7 :   while (NS_SUCCEEDED(mGetIdsForURIStatement->ExecuteStep(&hasMore)) &&
     262                 :          hasMore) {
     263                 :     PRInt64 downloadId;
     264               1 :     rv = mGetIdsForURIStatement->GetInt64(0, &downloadId);
     265               1 :     NS_ENSURE_SUCCESS(rv, rv);
     266                 : 
     267               1 :     downloads.AppendElement(downloadId);
     268                 :   }
     269                 : 
     270                 :   // Remove each download ignoring any failure so we reach other downloads
     271               7 :   for (PRInt32 i = downloads.Length(); --i >= 0; )
     272               1 :     (void)RemoveDownload(downloads[i]);
     273                 : 
     274               3 :   return NS_OK;
     275                 : }
     276                 : 
     277                 : void // static
     278               1 : nsDownloadManager::ResumeOnWakeCallback(nsITimer *aTimer, void *aClosure)
     279                 : {
     280                 :   // Resume the downloads that were set to autoResume
     281               1 :   nsDownloadManager *dlMgr = static_cast<nsDownloadManager *>(aClosure);
     282               1 :   (void)dlMgr->ResumeAllDownloads(false);
     283               1 : }
     284                 : 
     285                 : already_AddRefed<mozIStorageConnection>
     286              40 : nsDownloadManager::GetFileDBConnection(nsIFile *dbFile) const
     287                 : {
     288              40 :   NS_ASSERTION(dbFile, "GetFileDBConnection called with an invalid nsIFile");
     289                 : 
     290                 :   nsCOMPtr<mozIStorageService> storage =
     291              80 :     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
     292              40 :   NS_ENSURE_TRUE(storage, nsnull);
     293                 : 
     294              80 :   nsCOMPtr<mozIStorageConnection> conn;
     295              40 :   nsresult rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
     296              40 :   if (rv == NS_ERROR_FILE_CORRUPTED) {
     297                 :     // delete and try again, since we don't care so much about losing a user's
     298                 :     // download history
     299               0 :     rv = dbFile->Remove(false);
     300               0 :     NS_ENSURE_SUCCESS(rv, nsnull);
     301               0 :     rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
     302                 :   }
     303              40 :   NS_ENSURE_SUCCESS(rv, nsnull);
     304                 : 
     305              40 :   return conn.forget();
     306                 : }
     307                 : 
     308                 : already_AddRefed<mozIStorageConnection>
     309               6 : nsDownloadManager::GetMemoryDBConnection() const
     310                 : {
     311                 :   nsCOMPtr<mozIStorageService> storage =
     312              12 :     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
     313               6 :   NS_ENSURE_TRUE(storage, nsnull);
     314                 : 
     315              12 :   nsCOMPtr<mozIStorageConnection> conn;
     316               6 :   nsresult rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(conn));
     317               6 :   NS_ENSURE_SUCCESS(rv, nsnull);
     318                 : 
     319               6 :   return conn.forget();
     320                 : }
     321                 : 
     322                 : nsresult
     323               6 : nsDownloadManager::InitMemoryDB()
     324                 : {
     325               6 :   mDBConn = GetMemoryDBConnection();
     326               6 :   if (!mDBConn)
     327               0 :     return NS_ERROR_NOT_AVAILABLE;
     328                 : 
     329               6 :   nsresult rv = CreateTable();
     330               6 :   NS_ENSURE_SUCCESS(rv, rv);
     331                 : 
     332               6 :   mDBType = DATABASE_MEMORY;
     333               6 :   return NS_OK;
     334                 : }
     335                 : 
     336                 : nsresult
     337              40 : nsDownloadManager::InitFileDB()
     338                 : {
     339                 :   nsresult rv;
     340                 : 
     341              80 :   nsCOMPtr<nsIFile> dbFile;
     342                 :   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     343              40 :                               getter_AddRefs(dbFile));
     344              40 :   NS_ENSURE_SUCCESS(rv, rv);
     345              40 :   rv = dbFile->Append(DM_DB_NAME);
     346              40 :   NS_ENSURE_SUCCESS(rv, rv);
     347                 : 
     348              40 :   mDBConn = GetFileDBConnection(dbFile);
     349              40 :   NS_ENSURE_TRUE(mDBConn, NS_ERROR_NOT_AVAILABLE);
     350                 : 
     351                 :   bool tableExists;
     352              40 :   rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_downloads"), &tableExists);
     353              40 :   NS_ENSURE_SUCCESS(rv, rv);
     354              40 :   if (!tableExists) {
     355              23 :     rv = CreateTable();
     356              23 :     NS_ENSURE_SUCCESS(rv, rv);
     357              23 :     mDBType = DATABASE_DISK;
     358              23 :     return NS_OK;
     359                 :   }
     360                 : 
     361              17 :   mDBType = DATABASE_DISK;
     362                 : 
     363                 :   // Checking the database schema now
     364                 :   PRInt32 schemaVersion;
     365              17 :   rv = mDBConn->GetSchemaVersion(&schemaVersion);
     366              17 :   NS_ENSURE_SUCCESS(rv, rv);
     367                 : 
     368                 :   // Changing the database?  Be sure to do these two things!
     369                 :   // 1) Increment DM_SCHEMA_VERSION
     370                 :   // 2) Implement the proper downgrade/upgrade code for the current version
     371                 : 
     372              17 :   switch (schemaVersion) {
     373                 :   // Upgrading
     374                 :   // Every time you increment the database schema, you need to implement
     375                 :   // the upgrading code from the previous version to the new one.
     376                 :   // Also, don't forget to make a unit test to test your upgrading code!
     377                 :   case 1: // Drop a column (iconURL) from the database (bug 385875)
     378                 :     {
     379                 :       // Safely wrap this in a transaction so we don't hose the whole DB
     380               2 :       mozStorageTransaction safeTransaction(mDBConn, true);
     381                 : 
     382                 :       // Create a temporary table that will store the existing records
     383               2 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     384                 :         "CREATE TEMPORARY TABLE moz_downloads_backup ("
     385                 :           "id INTEGER PRIMARY KEY, "
     386                 :           "name TEXT, "
     387                 :           "source TEXT, "
     388                 :           "target TEXT, "
     389                 :           "startTime INTEGER, "
     390                 :           "endTime INTEGER, "
     391                 :           "state INTEGER"
     392               1 :         ")"));
     393               1 :       NS_ENSURE_SUCCESS(rv, rv);
     394                 : 
     395                 :       // Insert into a temporary table
     396               2 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     397                 :         "INSERT INTO moz_downloads_backup "
     398                 :         "SELECT id, name, source, target, startTime, endTime, state "
     399               1 :         "FROM moz_downloads"));
     400               1 :       NS_ENSURE_SUCCESS(rv, rv);
     401                 : 
     402                 :       // Drop the old table
     403               2 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     404               1 :         "DROP TABLE moz_downloads"));
     405               1 :       NS_ENSURE_SUCCESS(rv, rv);
     406                 : 
     407                 :       // Now recreate it with this schema version
     408               2 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     409                 :         "CREATE TABLE moz_downloads ("
     410                 :           "id INTEGER PRIMARY KEY, "
     411                 :           "name TEXT, "
     412                 :           "source TEXT, "
     413                 :           "target TEXT, "
     414                 :           "startTime INTEGER, "
     415                 :           "endTime INTEGER, "
     416                 :           "state INTEGER"
     417               1 :         ")"));
     418               1 :       NS_ENSURE_SUCCESS(rv, rv);
     419                 : 
     420                 :       // Insert the data back into it
     421               2 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     422                 :         "INSERT INTO moz_downloads "
     423                 :         "SELECT id, name, source, target, startTime, endTime, state "
     424               1 :         "FROM moz_downloads_backup"));
     425               1 :       NS_ENSURE_SUCCESS(rv, rv);
     426                 : 
     427                 :       // And drop our temporary table
     428               2 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     429               1 :         "DROP TABLE moz_downloads_backup"));
     430               1 :       NS_ENSURE_SUCCESS(rv, rv);
     431                 : 
     432                 :       // Finally, update the schemaVersion variable and the database schema
     433               1 :       schemaVersion = 2;
     434               1 :       rv = mDBConn->SetSchemaVersion(schemaVersion);
     435               1 :       NS_ENSURE_SUCCESS(rv, rv);
     436                 :     }
     437                 :     // Fallthrough to the next upgrade
     438                 : 
     439                 :   case 2: // Add referrer column to the database
     440                 :     {
     441               4 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     442                 :         "ALTER TABLE moz_downloads "
     443               2 :         "ADD COLUMN referrer TEXT"));
     444               2 :       NS_ENSURE_SUCCESS(rv, rv);
     445                 : 
     446                 :       // Finally, update the schemaVersion variable and the database schema
     447               2 :       schemaVersion = 3;
     448               2 :       rv = mDBConn->SetSchemaVersion(schemaVersion);
     449               2 :       NS_ENSURE_SUCCESS(rv, rv);
     450                 :     }
     451                 :     // Fallthrough to the next upgrade
     452                 : 
     453                 :   case 3: // This version adds a column to the database (entityID)
     454                 :     {
     455               6 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     456                 :         "ALTER TABLE moz_downloads "
     457               3 :         "ADD COLUMN entityID TEXT"));
     458               3 :       NS_ENSURE_SUCCESS(rv, rv);
     459                 : 
     460                 :       // Finally, update the schemaVersion variable and the database schema
     461               3 :       schemaVersion = 4;
     462               3 :       rv = mDBConn->SetSchemaVersion(schemaVersion);
     463               3 :       NS_ENSURE_SUCCESS(rv, rv);
     464                 :     }
     465                 :     // Fallthrough to the next upgrade
     466                 : 
     467                 :   case 4: // This version adds a column to the database (tempPath)
     468                 :     {
     469               6 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     470                 :         "ALTER TABLE moz_downloads "
     471               3 :         "ADD COLUMN tempPath TEXT"));
     472               3 :       NS_ENSURE_SUCCESS(rv, rv);
     473                 : 
     474                 :       // Finally, update the schemaVersion variable and the database schema
     475               3 :       schemaVersion = 5;
     476               3 :       rv = mDBConn->SetSchemaVersion(schemaVersion);
     477               3 :       NS_ENSURE_SUCCESS(rv, rv);
     478                 :     }
     479                 :     // Fallthrough to the next upgrade
     480                 : 
     481                 :   case 5: // This version adds two columns for tracking transfer progress
     482                 :     {
     483               8 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     484                 :         "ALTER TABLE moz_downloads "
     485               4 :         "ADD COLUMN currBytes INTEGER NOT NULL DEFAULT 0"));
     486               4 :       NS_ENSURE_SUCCESS(rv, rv);
     487                 : 
     488               8 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     489                 :         "ALTER TABLE moz_downloads "
     490               4 :         "ADD COLUMN maxBytes INTEGER NOT NULL DEFAULT -1"));
     491               4 :       NS_ENSURE_SUCCESS(rv, rv);
     492                 : 
     493                 :       // Finally, update the schemaVersion variable and the database schema
     494               4 :       schemaVersion = 6;
     495               4 :       rv = mDBConn->SetSchemaVersion(schemaVersion);
     496               4 :       NS_ENSURE_SUCCESS(rv, rv);
     497                 :     }
     498                 :     // Fallthrough to the next upgrade
     499                 : 
     500                 :   case 6: // This version adds three columns to DB (MIME type related info)
     501                 :     {
     502              12 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     503                 :         "ALTER TABLE moz_downloads "
     504               6 :         "ADD COLUMN mimeType TEXT"));
     505               6 :       NS_ENSURE_SUCCESS(rv, rv);
     506                 : 
     507              12 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     508                 :         "ALTER TABLE moz_downloads "
     509               6 :         "ADD COLUMN preferredApplication TEXT"));
     510               6 :       NS_ENSURE_SUCCESS(rv, rv);
     511                 : 
     512              12 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     513                 :         "ALTER TABLE moz_downloads "
     514               6 :         "ADD COLUMN preferredAction INTEGER NOT NULL DEFAULT 0"));
     515               6 :       NS_ENSURE_SUCCESS(rv, rv);
     516                 : 
     517                 :       // Finally, update the schemaVersion variable and the database schema
     518               6 :       schemaVersion = 7;
     519               6 :       rv = mDBConn->SetSchemaVersion(schemaVersion);
     520               6 :       NS_ENSURE_SUCCESS(rv, rv);
     521                 :     }
     522                 :     // Fallthrough to next upgrade
     523                 : 
     524                 :   case 7: // This version adds a column to remember to auto-resume downloads
     525                 :     {
     526              14 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     527                 :         "ALTER TABLE moz_downloads "
     528               7 :         "ADD COLUMN autoResume INTEGER NOT NULL DEFAULT 0"));
     529               7 :       NS_ENSURE_SUCCESS(rv, rv);
     530                 : 
     531                 :       // Finally, update the schemaVersion variable and the database schema
     532               7 :       schemaVersion = 8;
     533               7 :       rv = mDBConn->SetSchemaVersion(schemaVersion);
     534               7 :       NS_ENSURE_SUCCESS(rv, rv);
     535                 :     }
     536                 :     // Fallthrough to the next upgrade
     537                 : 
     538                 :   // Extra sanity checking for developers
     539                 : #ifndef DEBUG
     540                 :   case DM_SCHEMA_VERSION:
     541                 : #endif
     542               7 :     break;
     543                 : 
     544                 :   case 0:
     545                 :     {
     546               0 :       NS_WARNING("Could not get download database's schema version!");
     547                 : 
     548                 :       // The table may still be usable - someone may have just messed with the
     549                 :       // schema version, so let's just treat this like a downgrade and verify
     550                 :       // that the needed columns are there.  If they aren't there, we'll drop
     551                 :       // the table anyway.
     552               0 :       rv = mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
     553               0 :       NS_ENSURE_SUCCESS(rv, rv);
     554                 :     }
     555                 :     // Fallthrough to downgrade check
     556                 : 
     557                 :   // Downgrading
     558                 :   // If columns have been added to the table, we can still use the ones we
     559                 :   // understand safely.  If columns have been deleted or alterd, we just
     560                 :   // drop the table and start from scratch.  If you change how a column
     561                 :   // should be interpreted, make sure you also change its name so this
     562                 :   // check will catch it.
     563                 :   default:
     564                 :     {
     565              20 :       nsCOMPtr<mozIStorageStatement> stmt;
     566              20 :       rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     567                 :         "SELECT id, name, source, target, tempPath, startTime, endTime, state, "
     568                 :                "referrer, entityID, currBytes, maxBytes, mimeType, "
     569                 :                "preferredApplication, preferredAction, autoResume "
     570              20 :         "FROM moz_downloads"), getter_AddRefs(stmt));
     571              10 :       if (NS_SUCCEEDED(rv))
     572                 :         break;
     573                 : 
     574                 :       // if the statement fails, that means all the columns were not there.
     575                 :       // First we backup the database
     576                 :       nsCOMPtr<mozIStorageService> storage =
     577               0 :         do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
     578               0 :       NS_ENSURE_TRUE(storage, NS_ERROR_NOT_AVAILABLE);
     579               0 :       nsCOMPtr<nsIFile> backup;
     580               0 :       rv = storage->BackupDatabaseFile(dbFile, DM_DB_CORRUPT_FILENAME, nsnull,
     581               0 :                                        getter_AddRefs(backup));
     582               0 :       NS_ENSURE_SUCCESS(rv, rv);
     583                 : 
     584                 :       // Then we dump it
     585               0 :       rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     586               0 :         "DROP TABLE moz_downloads"));
     587               0 :       NS_ENSURE_SUCCESS(rv, rv);
     588                 : 
     589               0 :       rv = CreateTable();
     590               0 :       NS_ENSURE_SUCCESS(rv, rv);
     591                 :     }
     592               0 :     break;
     593                 :   }
     594                 : 
     595              17 :   return NS_OK;
     596                 : }
     597                 : 
     598                 : nsresult
     599              29 : nsDownloadManager::CreateTable()
     600                 : {
     601              29 :   nsresult rv = mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
     602              29 :   if (NS_FAILED(rv)) return rv;
     603                 : 
     604              58 :   return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     605                 :     "CREATE TABLE moz_downloads ("
     606                 :       "id INTEGER PRIMARY KEY, "
     607                 :       "name TEXT, "
     608                 :       "source TEXT, "
     609                 :       "target TEXT, "
     610                 :       "tempPath TEXT, "
     611                 :       "startTime INTEGER, "
     612                 :       "endTime INTEGER, "
     613                 :       "state INTEGER, "
     614                 :       "referrer TEXT, "
     615                 :       "entityID TEXT, "
     616                 :       "currBytes INTEGER NOT NULL DEFAULT 0, "
     617                 :       "maxBytes INTEGER NOT NULL DEFAULT -1, "
     618                 :       "mimeType TEXT, "
     619                 :       "preferredApplication TEXT, "
     620                 :       "preferredAction INTEGER NOT NULL DEFAULT 0, "
     621                 :       "autoResume INTEGER NOT NULL DEFAULT 0"
     622              29 :     ")"));
     623                 : }
     624                 : 
     625                 : nsresult
     626              46 : nsDownloadManager::RestoreDatabaseState()
     627                 : {
     628                 :   // Restore downloads that were in a scanning state.  We can assume that they
     629                 :   // have been dealt with by the virus scanner
     630              92 :   nsCOMPtr<mozIStorageStatement> stmt;
     631              92 :   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     632                 :     "UPDATE moz_downloads "
     633                 :     "SET state = :state "
     634              92 :     "WHERE state = :state_cond"), getter_AddRefs(stmt));
     635              46 :   NS_ENSURE_SUCCESS(rv, rv);
     636                 : 
     637              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
     638              46 :   NS_ENSURE_SUCCESS(rv, rv);
     639              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state_cond"), nsIDownloadManager::DOWNLOAD_SCANNING);
     640              46 :   NS_ENSURE_SUCCESS(rv, rv);
     641                 : 
     642              46 :   rv = stmt->Execute();
     643              46 :   NS_ENSURE_SUCCESS(rv, rv);
     644                 : 
     645                 :   // Convert supposedly-active downloads into downloads that should auto-resume
     646              92 :   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     647                 :     "UPDATE moz_downloads "
     648                 :     "SET autoResume = :autoResume "
     649                 :     "WHERE state = :notStarted "
     650                 :       "OR state = :queued "
     651              92 :       "OR state = :downloading"), getter_AddRefs(stmt));
     652              46 :   NS_ENSURE_SUCCESS(rv, rv);
     653                 : 
     654              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::AUTO_RESUME);
     655              46 :   NS_ENSURE_SUCCESS(rv, rv);
     656              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("notStarted"), nsIDownloadManager::DOWNLOAD_NOTSTARTED);
     657              46 :   NS_ENSURE_SUCCESS(rv, rv);
     658              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
     659              46 :   NS_ENSURE_SUCCESS(rv, rv);
     660              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING);
     661              46 :   NS_ENSURE_SUCCESS(rv, rv);
     662                 : 
     663              46 :   rv = stmt->Execute();
     664              46 :   NS_ENSURE_SUCCESS(rv, rv);
     665                 : 
     666                 :   // Switch any download that is supposed to automatically resume and is in a
     667                 :   // finished state to *not* automatically resume.  See Bug 409179 for details.
     668              92 :   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     669                 :     "UPDATE moz_downloads "
     670                 :     "SET autoResume = :autoResume "
     671                 :     "WHERE state = :state "
     672                 :       "AND autoResume = :autoResume_cond"),
     673              92 :     getter_AddRefs(stmt));
     674              46 :   NS_ENSURE_SUCCESS(rv, rv);
     675                 : 
     676              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME);
     677              46 :   NS_ENSURE_SUCCESS(rv, rv);
     678              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
     679              46 :   NS_ENSURE_SUCCESS(rv, rv);
     680              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume_cond"), nsDownload::AUTO_RESUME);
     681              46 :   NS_ENSURE_SUCCESS(rv, rv);
     682                 : 
     683              46 :   rv = stmt->Execute();
     684              46 :   NS_ENSURE_SUCCESS(rv, rv);
     685                 : 
     686              46 :   return NS_OK;
     687                 : }
     688                 : 
     689                 : nsresult
     690              46 : nsDownloadManager::RestoreActiveDownloads()
     691                 : {
     692              92 :   nsCOMPtr<mozIStorageStatement> stmt;
     693              92 :   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     694                 :     "SELECT id "
     695                 :     "FROM moz_downloads "
     696                 :     "WHERE (state = :state AND LENGTH(entityID) > 0) "
     697              92 :       "OR autoResume != :autoResume"), getter_AddRefs(stmt));
     698              46 :   NS_ENSURE_SUCCESS(rv, rv);
     699                 : 
     700              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_PAUSED);
     701              46 :   NS_ENSURE_SUCCESS(rv, rv);
     702              46 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME);
     703              46 :   NS_ENSURE_SUCCESS(rv, rv);
     704                 : 
     705              46 :   nsresult retVal = NS_OK;
     706                 :   bool hasResults;
     707              99 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResults)) && hasResults) {
     708              14 :     nsRefPtr<nsDownload> dl;
     709                 :     // Keep trying to add even if we fail one, but make sure to return failure.
     710                 :     // Additionally, be careful to not call anything that tries to change the
     711                 :     // database because we're iterating over a live statement.
     712              14 :     if (NS_FAILED(GetDownloadFromDB(stmt->AsInt32(0), getter_AddRefs(dl))) ||
     713               7 :         NS_FAILED(AddToCurrentDownloads(dl)))
     714               0 :       retVal = NS_ERROR_FAILURE;
     715                 :   }
     716                 : 
     717                 :   // Try to resume only the downloads that should auto-resume
     718              46 :   rv = ResumeAllDownloads(false);
     719              46 :   NS_ENSURE_SUCCESS(rv, rv);
     720                 : 
     721              46 :   return retVal;
     722                 : }
     723                 : 
     724                 : PRInt64
     725              20 : nsDownloadManager::AddDownloadToDB(const nsAString &aName,
     726                 :                                    const nsACString &aSource,
     727                 :                                    const nsACString &aTarget,
     728                 :                                    const nsAString &aTempPath,
     729                 :                                    PRInt64 aStartTime,
     730                 :                                    PRInt64 aEndTime,
     731                 :                                    const nsACString &aMimeType,
     732                 :                                    const nsACString &aPreferredApp,
     733                 :                                    nsHandlerInfoAction aPreferredAction)
     734                 : {
     735              40 :   nsCOMPtr<mozIStorageStatement> stmt;
     736              40 :   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     737                 :     "INSERT INTO moz_downloads "
     738                 :     "(name, source, target, tempPath, startTime, endTime, state, "
     739                 :      "mimeType, preferredApplication, preferredAction) VALUES "
     740                 :     "(:name, :source, :target, :tempPath, :startTime, :endTime, :state, "
     741                 :      ":mimeType, :preferredApplication, :preferredAction)"),
     742              40 :     getter_AddRefs(stmt));
     743              20 :   NS_ENSURE_SUCCESS(rv, 0);
     744                 : 
     745              20 :   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
     746              20 :   NS_ENSURE_SUCCESS(rv, 0);
     747                 : 
     748              20 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("source"), aSource);
     749              20 :   NS_ENSURE_SUCCESS(rv, 0);
     750                 : 
     751              20 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("target"), aTarget);
     752              20 :   NS_ENSURE_SUCCESS(rv, 0);
     753                 : 
     754              20 :   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), aTempPath);
     755              20 :   NS_ENSURE_SUCCESS(rv, 0);
     756                 : 
     757              20 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
     758              20 :   NS_ENSURE_SUCCESS(rv, 0);
     759                 : 
     760              20 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime);
     761              20 :   NS_ENSURE_SUCCESS(rv, 0);
     762                 : 
     763              20 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_NOTSTARTED);
     764              20 :   NS_ENSURE_SUCCESS(rv, 0);
     765                 : 
     766              20 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mimeType"), aMimeType);
     767              20 :   NS_ENSURE_SUCCESS(rv, 0);
     768                 : 
     769              20 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("preferredApplication"), aPreferredApp);
     770              20 :   NS_ENSURE_SUCCESS(rv, 0);
     771                 : 
     772              20 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("preferredAction"), aPreferredAction);
     773              20 :   NS_ENSURE_SUCCESS(rv, 0);
     774                 : 
     775                 :   bool hasMore;
     776              20 :   rv = stmt->ExecuteStep(&hasMore); // we want to keep our lock
     777              20 :   NS_ENSURE_SUCCESS(rv, 0);
     778                 : 
     779              20 :   PRInt64 id = 0;
     780              20 :   rv = mDBConn->GetLastInsertRowID(&id);
     781              20 :   NS_ENSURE_SUCCESS(rv, 0);
     782                 : 
     783                 :   // lock on DB from statement will be released once we return
     784              20 :   return id;
     785                 : }
     786                 : 
     787                 : nsresult
     788              46 : nsDownloadManager::InitDB()
     789                 : {
     790              46 :   nsresult rv = NS_OK;
     791                 : 
     792              46 :   switch (mDBType) {
     793                 :     case DATABASE_MEMORY:
     794               6 :       rv = InitMemoryDB();
     795               6 :       break;
     796                 : 
     797                 :     case DATABASE_DISK:
     798              40 :       rv = InitFileDB();
     799              40 :       break;
     800                 : 
     801                 :     default:
     802               0 :       NS_ERROR("Unexpected value encountered for nsDownloadManager::mDBType");
     803               0 :       break;
     804                 :   }
     805              46 :   NS_ENSURE_SUCCESS(rv, rv);
     806                 : 
     807              92 :   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     808                 :     "UPDATE moz_downloads "
     809                 :     "SET tempPath = :tempPath, startTime = :startTime, endTime = :endTime, "
     810                 :       "state = :state, referrer = :referrer, entityID = :entityID, "
     811                 :       "currBytes = :currBytes, maxBytes = :maxBytes, autoResume = :autoResume "
     812              92 :     "WHERE id = :id"), getter_AddRefs(mUpdateDownloadStatement));
     813              46 :   NS_ENSURE_SUCCESS(rv, rv);
     814                 : 
     815              92 :   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     816                 :     "SELECT id "
     817                 :     "FROM moz_downloads "
     818              92 :     "WHERE source = :source"), getter_AddRefs(mGetIdsForURIStatement));
     819              46 :   NS_ENSURE_SUCCESS(rv, rv);
     820                 : 
     821              46 :   return rv;
     822                 : }
     823                 : 
     824                 : nsresult
     825              34 : nsDownloadManager::Init()
     826                 : {
     827                 :   // Clean up any old downloads.rdf files from before Firefox 3
     828                 :   {
     829              68 :     nsCOMPtr<nsIFile> oldDownloadsFile;
     830                 :     bool fileExists;
     831              54 :     if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE,
     832                 :           getter_AddRefs(oldDownloadsFile))) &&
     833              20 :         NS_SUCCEEDED(oldDownloadsFile->Exists(&fileExists)) &&
     834                 :         fileExists) {
     835               1 :       (void)oldDownloadsFile->Remove(false);
     836                 :     }
     837                 :   }
     838                 : 
     839              34 :   mObserverService = mozilla::services::GetObserverService();
     840              34 :   if (!mObserverService)
     841               0 :     return NS_ERROR_FAILURE;
     842                 : 
     843                 :   nsCOMPtr<nsIStringBundleService> bundleService =
     844              68 :     mozilla::services::GetStringBundleService();
     845              34 :   if (!bundleService)
     846               0 :     return NS_ERROR_FAILURE;
     847                 : 
     848              34 :   nsresult rv = InitDB();
     849              34 :   NS_ENSURE_SUCCESS(rv, rv);
     850                 : 
     851              34 :   rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE,
     852              34 :                                    getter_AddRefs(mBundle));
     853              34 :   NS_ENSURE_SUCCESS(rv, rv);
     854                 : 
     855                 : #ifdef DOWNLOAD_SCANNER
     856                 :   mScanner = new nsDownloadScanner();
     857                 :   if (!mScanner)
     858                 :     return NS_ERROR_OUT_OF_MEMORY;
     859                 :   rv = mScanner->Init();
     860                 :   if (NS_FAILED(rv)) {
     861                 :     delete mScanner;
     862                 :     mScanner = nsnull;
     863                 :   }
     864                 : #endif
     865                 : 
     866                 :   // Do things *after* initializing various download manager properties such as
     867                 :   // restoring downloads to a consistent state
     868              34 :   rv = RestoreDatabaseState();
     869              34 :   NS_ENSURE_SUCCESS(rv, rv);
     870                 : 
     871              34 :   rv = RestoreActiveDownloads();
     872              34 :   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
     873                 : 
     874                 :   nsCOMPtr<nsIPrivateBrowsingService> pbs =
     875              68 :     do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
     876              34 :   if (pbs) {
     877              34 :     (void)pbs->GetPrivateBrowsingEnabled(&mInPrivateBrowsing);
     878              34 :     if (mInPrivateBrowsing)
     879               0 :       OnEnterPrivateBrowsingMode();
     880                 :   }
     881                 : 
     882                 :   nsCOMPtr<nsINavHistoryService> history =
     883              68 :     do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
     884                 : 
     885                 :   // The following AddObserver calls must be the last lines in this function,
     886                 :   // because otherwise, this function may fail (and thus, this object would be not
     887                 :   // completely initialized), but the observerservice would still keep a reference
     888                 :   // to us and notify us about shutdown, which may cause crashes.
     889                 :   // failure to add an observer is not critical
     890              34 :   (void)mObserverService->AddObserver(this, "quit-application", true);
     891              34 :   (void)mObserverService->AddObserver(this, "quit-application-requested", true);
     892              34 :   (void)mObserverService->AddObserver(this, "offline-requested", true);
     893              34 :   (void)mObserverService->AddObserver(this, "sleep_notification", true);
     894              34 :   (void)mObserverService->AddObserver(this, "wake_notification", true);
     895              34 :   (void)mObserverService->AddObserver(this, "profile-before-change", true);
     896              34 :   (void)mObserverService->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, true);
     897              34 :   (void)mObserverService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, true);
     898              34 :   (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_REQUEST_TOPIC, true);
     899              34 :   (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
     900                 : 
     901              34 :   if (history)
     902              34 :     (void)history->AddObserver(this, true);
     903                 : 
     904              34 :   return NS_OK;
     905                 : }
     906                 : 
     907                 : PRInt32
     908              48 : nsDownloadManager::GetRetentionBehavior()
     909                 : {
     910                 :   // We use 0 as the default, which is "remove when done"
     911                 :   nsresult rv;
     912              96 :   nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
     913              48 :   NS_ENSURE_SUCCESS(rv, 0);
     914                 : 
     915                 :   PRInt32 val;
     916              48 :   rv = pref->GetIntPref(PREF_BDM_RETENTION, &val);
     917              48 :   NS_ENSURE_SUCCESS(rv, 0);
     918                 : 
     919              48 :   return val;
     920                 : }
     921                 : 
     922                 : enum nsDownloadManager::QuitBehavior
     923             204 : nsDownloadManager::GetQuitBehavior()
     924                 : {
     925                 :   // We use 0 as the default, which is "remember and resume the download"
     926                 :   nsresult rv;
     927             408 :   nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
     928             204 :   NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME);
     929                 : 
     930                 :   PRInt32 val;
     931             204 :   rv = pref->GetIntPref(PREF_BDM_QUITBEHAVIOR, &val);
     932             204 :   NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME);
     933                 : 
     934             204 :   switch (val) {
     935                 :     case 1:
     936               0 :       return QUIT_AND_PAUSE;
     937                 :     case 2:
     938               0 :       return QUIT_AND_CANCEL;
     939                 :     default:
     940             204 :       return QUIT_AND_RESUME;
     941                 :   }
     942                 : }
     943                 : 
     944                 : nsresult
     945              18 : nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
     946                 : {
     947              18 :   NS_ASSERTION(!FindDownload(aID),
     948                 :                "If it is a current download, you should not call this method!");
     949                 : 
     950                 :   // First, let's query the database and see if it even exists
     951              36 :   nsCOMPtr<mozIStorageStatement> stmt;
     952              36 :   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     953                 :     "SELECT id, state, startTime, source, target, tempPath, name, referrer, "
     954                 :            "entityID, currBytes, maxBytes, mimeType, preferredAction, "
     955                 :            "preferredApplication, autoResume "
     956                 :     "FROM moz_downloads "
     957              36 :     "WHERE id = :id"), getter_AddRefs(stmt));
     958              18 :   NS_ENSURE_SUCCESS(rv, rv);
     959                 : 
     960              18 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID);
     961              18 :   NS_ENSURE_SUCCESS(rv, rv);
     962                 : 
     963              18 :   bool hasResults = false;
     964              18 :   rv = stmt->ExecuteStep(&hasResults);
     965              18 :   if (NS_FAILED(rv) || !hasResults)
     966               3 :     return NS_ERROR_NOT_AVAILABLE;
     967                 : 
     968                 :   // We have a download, so lets create it
     969              30 :   nsRefPtr<nsDownload> dl = new nsDownload();
     970              15 :   if (!dl)
     971               0 :     return NS_ERROR_OUT_OF_MEMORY;
     972                 : 
     973              15 :   PRInt32 i = 0;
     974                 :   // Setting all properties of the download now
     975              15 :   dl->mCancelable = nsnull;
     976              15 :   dl->mID = stmt->AsInt64(i++);
     977              15 :   dl->mDownloadState = stmt->AsInt32(i++);
     978              15 :   dl->mStartTime = stmt->AsInt64(i++);
     979                 : 
     980              30 :   nsCString source;
     981              15 :   stmt->GetUTF8String(i++, source);
     982              15 :   rv = NS_NewURI(getter_AddRefs(dl->mSource), source);
     983              15 :   NS_ENSURE_SUCCESS(rv, rv);
     984                 : 
     985              30 :   nsCString target;
     986              15 :   stmt->GetUTF8String(i++, target);
     987              15 :   rv = NS_NewURI(getter_AddRefs(dl->mTarget), target);
     988              15 :   NS_ENSURE_SUCCESS(rv, rv);
     989                 : 
     990              30 :   nsString tempPath;
     991              15 :   stmt->GetString(i++, tempPath);
     992              15 :   if (!tempPath.IsEmpty()) {
     993               0 :     rv = NS_NewLocalFile(tempPath, true, getter_AddRefs(dl->mTempFile));
     994               0 :     NS_ENSURE_SUCCESS(rv, rv);
     995                 :   }
     996                 : 
     997              15 :   stmt->GetString(i++, dl->mDisplayName);
     998                 : 
     999              30 :   nsCString referrer;
    1000              15 :   rv = stmt->GetUTF8String(i++, referrer);
    1001              15 :   if (NS_SUCCEEDED(rv) && !referrer.IsEmpty()) {
    1002               1 :     rv = NS_NewURI(getter_AddRefs(dl->mReferrer), referrer);
    1003               1 :     NS_ENSURE_SUCCESS(rv, rv);
    1004                 :   }
    1005                 : 
    1006              15 :   rv = stmt->GetUTF8String(i++, dl->mEntityID);
    1007              15 :   NS_ENSURE_SUCCESS(rv, rv);
    1008                 : 
    1009              15 :   PRInt64 currBytes = stmt->AsInt64(i++);
    1010              15 :   PRInt64 maxBytes = stmt->AsInt64(i++);
    1011              15 :   dl->SetProgressBytes(currBytes, maxBytes);
    1012                 : 
    1013                 :   // Build mMIMEInfo only if the mimeType in DB is not empty
    1014              30 :   nsCAutoString mimeType;
    1015              15 :   rv = stmt->GetUTF8String(i++, mimeType);
    1016              15 :   NS_ENSURE_SUCCESS(rv, rv);
    1017                 : 
    1018              15 :   if (!mimeType.IsEmpty()) {
    1019                 :     nsCOMPtr<nsIMIMEService> mimeService =
    1020               0 :       do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
    1021               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1022                 : 
    1023               0 :     rv = mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
    1024               0 :                                               getter_AddRefs(dl->mMIMEInfo));
    1025               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1026                 : 
    1027               0 :     nsHandlerInfoAction action = stmt->AsInt32(i++);
    1028               0 :     rv = dl->mMIMEInfo->SetPreferredAction(action);
    1029               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1030                 : 
    1031               0 :     nsCAutoString persistentDescriptor;
    1032               0 :     rv = stmt->GetUTF8String(i++, persistentDescriptor);
    1033               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1034                 : 
    1035               0 :     if (!persistentDescriptor.IsEmpty()) {
    1036                 :       nsCOMPtr<nsILocalHandlerApp> handler =
    1037               0 :         do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
    1038               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1039                 : 
    1040               0 :       nsCOMPtr<nsILocalFile> localExecutable;
    1041               0 :       rv = NS_NewNativeLocalFile(EmptyCString(), false,
    1042               0 :                                  getter_AddRefs(localExecutable));
    1043               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1044                 : 
    1045               0 :       rv = localExecutable->SetPersistentDescriptor(persistentDescriptor);
    1046               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1047                 : 
    1048               0 :       rv = handler->SetExecutable(localExecutable);
    1049               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1050                 : 
    1051               0 :       rv = dl->mMIMEInfo->SetPreferredApplicationHandler(handler);
    1052               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1053                 :     }
    1054                 :   } else {
    1055                 :     // Compensate for the i++s skipped in the true block
    1056              15 :     i += 2;
    1057                 :   }
    1058                 : 
    1059              15 :   dl->mAutoResume =
    1060              15 :     static_cast<enum nsDownload::AutoResume>(stmt->AsInt32(i++));
    1061                 : 
    1062                 :   // Addrefing and returning
    1063              15 :   NS_ADDREF(*retVal = dl);
    1064              15 :   return NS_OK;
    1065                 : }
    1066                 : 
    1067                 : nsresult
    1068              29 : nsDownloadManager::AddToCurrentDownloads(nsDownload *aDl)
    1069                 : {
    1070              29 :   if (!mCurrentDownloads.AppendObject(aDl))
    1071               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1072                 : 
    1073              29 :   aDl->mDownloadManager = this;
    1074              29 :   return NS_OK;
    1075                 : }
    1076                 : 
    1077                 : void
    1078              37 : nsDownloadManager::SendEvent(nsDownload *aDownload, const char *aTopic)
    1079                 : {
    1080              37 :   (void)mObserverService->NotifyObservers(aDownload, aTopic, nsnull);
    1081              37 : }
    1082                 : 
    1083                 : ////////////////////////////////////////////////////////////////////////////////
    1084                 : //// nsIDownloadManager
    1085                 : 
    1086                 : NS_IMETHODIMP
    1087              15 : nsDownloadManager::GetActiveDownloadCount(PRInt32 *aResult)
    1088                 : {
    1089              15 :   *aResult = mCurrentDownloads.Count();
    1090                 : 
    1091              15 :   return NS_OK;
    1092                 : }
    1093                 : 
    1094                 : NS_IMETHODIMP
    1095              64 : nsDownloadManager::GetActiveDownloads(nsISimpleEnumerator **aResult)
    1096                 : {
    1097              64 :   return NS_NewArrayEnumerator(aResult, mCurrentDownloads);
    1098                 : }
    1099                 : 
    1100                 : /**
    1101                 :  * For platforms where helper apps use the downloads directory (i.e. mobile),
    1102                 :  * this should be kept in sync with nsExternalHelperAppService.cpp
    1103                 :  */
    1104                 : NS_IMETHODIMP
    1105               9 : nsDownloadManager::GetDefaultDownloadsDirectory(nsILocalFile **aResult)
    1106                 : {
    1107              18 :   nsCOMPtr<nsILocalFile> downloadDir;
    1108                 : 
    1109                 :   nsresult rv;
    1110                 :   nsCOMPtr<nsIProperties> dirService =
    1111              18 :      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
    1112               9 :   NS_ENSURE_SUCCESS(rv, rv);
    1113                 : 
    1114                 :   // OSX 10.4:
    1115                 :   // Desktop
    1116                 :   // OSX 10.5:
    1117                 :   // User download directory
    1118                 :   // Vista:
    1119                 :   // Downloads
    1120                 :   // XP/2K:
    1121                 :   // My Documents/Downloads
    1122                 :   // Linux:
    1123                 :   // XDG user dir spec, with a fallback to Home/Downloads
    1124                 : 
    1125              18 :   nsXPIDLString folderName;
    1126              18 :   mBundle->GetStringFromName(NS_LITERAL_STRING("downloadsFolder").get(),
    1127              18 :                              getter_Copies(folderName));
    1128                 : 
    1129                 : #if defined (XP_MACOSX)
    1130                 :   rv = dirService->Get(NS_OSX_DEFAULT_DOWNLOAD_DIR,
    1131                 :                        NS_GET_IID(nsILocalFile),
    1132                 :                        getter_AddRefs(downloadDir));
    1133                 :   NS_ENSURE_SUCCESS(rv, rv);
    1134                 : #elif defined(XP_WIN)
    1135                 :   rv = dirService->Get(NS_WIN_DEFAULT_DOWNLOAD_DIR,
    1136                 :                        NS_GET_IID(nsILocalFile),
    1137                 :                        getter_AddRefs(downloadDir));
    1138                 :   NS_ENSURE_SUCCESS(rv, rv);
    1139                 : 
    1140                 :   // Check the os version
    1141                 :   nsCOMPtr<nsIPropertyBag2> infoService =
    1142                 :      do_GetService(NS_SYSTEMINFO_CONTRACTID, &rv);
    1143                 :   NS_ENSURE_SUCCESS(rv, rv);
    1144                 : 
    1145                 :   PRInt32 version;
    1146                 :   NS_NAMED_LITERAL_STRING(osVersion, "version");
    1147                 :   rv = infoService->GetPropertyAsInt32(osVersion, &version);
    1148                 :   NS_ENSURE_SUCCESS(rv, rv);
    1149                 :   if (version < 6) { // XP/2K
    1150                 :     // First get "My Documents"
    1151                 :     rv = dirService->Get(NS_WIN_PERSONAL_DIR,
    1152                 :                          NS_GET_IID(nsILocalFile),
    1153                 :                          getter_AddRefs(downloadDir));
    1154                 :     NS_ENSURE_SUCCESS(rv, rv);
    1155                 : 
    1156                 :     rv = downloadDir->Append(folderName);
    1157                 :     NS_ENSURE_SUCCESS(rv, rv);
    1158                 : 
    1159                 :     // This could be the first time we are creating the downloads folder in My
    1160                 :     // Documents, so make sure it exists.
    1161                 :     bool exists;
    1162                 :     rv = downloadDir->Exists(&exists);
    1163                 :     NS_ENSURE_SUCCESS(rv, rv);
    1164                 :     if (!exists) {
    1165                 :       rv = downloadDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
    1166                 :       NS_ENSURE_SUCCESS(rv, rv);
    1167                 :     }
    1168                 :   }
    1169                 : #elif defined(XP_UNIX)
    1170                 : #if defined(MOZ_PLATFORM_MAEMO)
    1171                 :     // As maemo does not follow the XDG "standard" (as usually desktop
    1172                 :     // Linux distros do) neither has a working $HOME/Desktop folder
    1173                 :     // for us to fallback into, "$HOME/MyDocs/.documents/" is the folder
    1174                 :     // we found most apropriate to be the default target folder for downloads
    1175                 :     // on the platform.
    1176                 :     rv = dirService->Get(NS_UNIX_XDG_DOCUMENTS_DIR,
    1177                 :                          NS_GET_IID(nsILocalFile),
    1178                 :                          getter_AddRefs(downloadDir));
    1179                 : #elif defined(MOZ_WIDGET_ANDROID)
    1180                 :     // Android doesn't have a $HOME directory, and by default we only have
    1181                 :     // write access to /data/data/org.mozilla.{$APP} and /sdcard
    1182                 :     char* downloadDirPath = getenv("DOWNLOADS_DIRECTORY");
    1183                 :     if (downloadDirPath) {
    1184                 :       rv = NS_NewNativeLocalFile(nsDependentCString(downloadDirPath),
    1185                 :                                  true, getter_AddRefs(downloadDir));
    1186                 :       NS_ENSURE_SUCCESS(rv, rv);
    1187                 :     }
    1188                 :     else {
    1189                 :       rv = NS_ERROR_FAILURE;
    1190                 :     }
    1191                 : #else
    1192               9 :   rv = dirService->Get(NS_UNIX_DEFAULT_DOWNLOAD_DIR,
    1193                 :                        NS_GET_IID(nsILocalFile),
    1194               9 :                        getter_AddRefs(downloadDir));
    1195                 :   // fallback to Home/Downloads
    1196               9 :   if (NS_FAILED(rv)) {
    1197               9 :     rv = dirService->Get(NS_UNIX_HOME_DIR,
    1198                 :                          NS_GET_IID(nsILocalFile),
    1199               9 :                          getter_AddRefs(downloadDir));
    1200               9 :     NS_ENSURE_SUCCESS(rv, rv);
    1201               9 :     rv = downloadDir->Append(folderName);
    1202               9 :     NS_ENSURE_SUCCESS(rv, rv);
    1203                 :   }
    1204                 : #endif
    1205                 : #else
    1206                 :   rv = dirService->Get(NS_OS_HOME_DIR,
    1207                 :                        NS_GET_IID(nsILocalFile),
    1208                 :                        getter_AddRefs(downloadDir));
    1209                 :   NS_ENSURE_SUCCESS(rv, rv);
    1210                 :   rv = downloadDir->Append(folderName);
    1211                 :   NS_ENSURE_SUCCESS(rv, rv);
    1212                 : #endif
    1213                 : 
    1214               9 :   downloadDir.forget(aResult);
    1215                 : 
    1216               9 :   return NS_OK;
    1217                 : }
    1218                 : 
    1219                 : #define NS_BRANCH_DOWNLOAD     "browser.download."
    1220                 : #define NS_PREF_FOLDERLIST     "folderList"
    1221                 : #define NS_PREF_DIR            "dir"
    1222                 : 
    1223                 : NS_IMETHODIMP
    1224               9 : nsDownloadManager::GetUserDownloadsDirectory(nsILocalFile **aResult)
    1225                 : {
    1226                 :   nsresult rv;
    1227                 :   nsCOMPtr<nsIProperties> dirService =
    1228              18 :      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
    1229               9 :   NS_ENSURE_SUCCESS(rv, rv);
    1230                 : 
    1231                 :   nsCOMPtr<nsIPrefService> prefService =
    1232              18 :      do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
    1233               9 :   NS_ENSURE_SUCCESS(rv, rv);
    1234                 : 
    1235              18 :   nsCOMPtr<nsIPrefBranch> prefBranch;
    1236               9 :   rv = prefService->GetBranch(NS_BRANCH_DOWNLOAD,
    1237               9 :                               getter_AddRefs(prefBranch));
    1238               9 :   NS_ENSURE_SUCCESS(rv, rv);
    1239                 : 
    1240                 :   PRInt32 val;
    1241               9 :   rv = prefBranch->GetIntPref(NS_PREF_FOLDERLIST,
    1242               9 :                               &val);
    1243               9 :   NS_ENSURE_SUCCESS(rv, rv);
    1244                 : 
    1245               9 :   switch(val) {
    1246                 :     case 0: // Desktop
    1247                 :       {
    1248               0 :         nsCOMPtr<nsILocalFile> downloadDir;
    1249                 :         nsCOMPtr<nsIProperties> dirService =
    1250               0 :            do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
    1251               0 :         NS_ENSURE_SUCCESS(rv, rv);
    1252               0 :         rv = dirService->Get(NS_OS_DESKTOP_DIR,
    1253                 :                              NS_GET_IID(nsILocalFile),
    1254               0 :                              getter_AddRefs(downloadDir));
    1255               0 :         NS_ENSURE_SUCCESS(rv, rv);
    1256               0 :         downloadDir.forget(aResult);
    1257               0 :         return NS_OK;
    1258                 :       }
    1259                 :       break;
    1260                 :     case 1: // Downloads
    1261               9 :       return GetDefaultDownloadsDirectory(aResult);
    1262                 :     case 2: // Custom
    1263                 :       {
    1264               0 :         nsCOMPtr<nsILocalFile> customDirectory;
    1265               0 :         prefBranch->GetComplexValue(NS_PREF_DIR,
    1266                 :                                     NS_GET_IID(nsILocalFile),
    1267               0 :                                     getter_AddRefs(customDirectory));
    1268               0 :         if (customDirectory) {
    1269               0 :           bool exists = false;
    1270               0 :           (void)customDirectory->Exists(&exists);
    1271                 : 
    1272               0 :           if (!exists) {
    1273               0 :             rv = customDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
    1274               0 :             if (NS_SUCCEEDED(rv)) {
    1275               0 :               customDirectory.forget(aResult);
    1276               0 :               return NS_OK;
    1277                 :             }
    1278                 : 
    1279                 :             // Create failed, so it still doesn't exist.  Fall out and get the
    1280                 :             // default downloads directory.
    1281                 :           }
    1282                 : 
    1283               0 :           bool writable = false;
    1284               0 :           bool directory = false;
    1285               0 :           (void)customDirectory->IsWritable(&writable);
    1286               0 :           (void)customDirectory->IsDirectory(&directory);
    1287                 : 
    1288               0 :           if (exists && writable && directory) {
    1289               0 :             customDirectory.forget(aResult);
    1290               0 :             return NS_OK;
    1291                 :           }
    1292                 :         }
    1293               0 :         rv = GetDefaultDownloadsDirectory(aResult);
    1294               0 :         if (NS_SUCCEEDED(rv)) {
    1295               0 :           (void)prefBranch->SetComplexValue(NS_PREF_DIR,
    1296                 :                                             NS_GET_IID(nsILocalFile),
    1297               0 :                                             *aResult);
    1298                 :         }
    1299               0 :         return rv;
    1300                 :       }
    1301                 :       break;
    1302                 :   }
    1303               0 :   return NS_ERROR_INVALID_ARG;
    1304                 : }
    1305                 : 
    1306                 : NS_IMETHODIMP
    1307              20 : nsDownloadManager::AddDownload(DownloadType aDownloadType,
    1308                 :                                nsIURI *aSource,
    1309                 :                                nsIURI *aTarget,
    1310                 :                                const nsAString& aDisplayName,
    1311                 :                                nsIMIMEInfo *aMIMEInfo,
    1312                 :                                PRTime aStartTime,
    1313                 :                                nsILocalFile *aTempFile,
    1314                 :                                nsICancelable *aCancelable,
    1315                 :                                nsIDownload **aDownload)
    1316                 : {
    1317              20 :   NS_ENSURE_ARG_POINTER(aSource);
    1318              20 :   NS_ENSURE_ARG_POINTER(aTarget);
    1319              20 :   NS_ENSURE_ARG_POINTER(aDownload);
    1320                 : 
    1321                 :   nsresult rv;
    1322                 : 
    1323                 :   // target must be on the local filesystem
    1324              40 :   nsCOMPtr<nsIFileURL> targetFileURL = do_QueryInterface(aTarget, &rv);
    1325              20 :   NS_ENSURE_SUCCESS(rv, rv);
    1326                 : 
    1327              40 :   nsCOMPtr<nsIFile> targetFile;
    1328              20 :   rv = targetFileURL->GetFile(getter_AddRefs(targetFile));
    1329              20 :   NS_ENSURE_SUCCESS(rv, rv);
    1330                 : 
    1331              40 :   nsRefPtr<nsDownload> dl = new nsDownload();
    1332              20 :   if (!dl)
    1333               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1334                 : 
    1335                 :   // give our new nsIDownload some info so it's ready to go off into the world
    1336              20 :   dl->mTarget = aTarget;
    1337              20 :   dl->mSource = aSource;
    1338              20 :   dl->mTempFile = aTempFile;
    1339                 : 
    1340              20 :   dl->mDisplayName = aDisplayName;
    1341              20 :   if (dl->mDisplayName.IsEmpty())
    1342              14 :     targetFile->GetLeafName(dl->mDisplayName);
    1343                 : 
    1344              20 :   dl->mMIMEInfo = aMIMEInfo;
    1345              20 :   dl->SetStartTime(aStartTime == 0 ? PR_Now() : aStartTime);
    1346                 : 
    1347                 :   // Creates a cycle that will be broken when the download finishes
    1348              20 :   dl->mCancelable = aCancelable;
    1349                 : 
    1350                 :   // Adding to the DB
    1351              40 :   nsCAutoString source, target;
    1352              20 :   aSource->GetSpec(source);
    1353              20 :   aTarget->GetSpec(target);
    1354                 : 
    1355                 :   // Track the temp file for exthandler downloads
    1356              40 :   nsAutoString tempPath;
    1357              20 :   if (aTempFile)
    1358               6 :     aTempFile->GetPath(tempPath);
    1359                 : 
    1360                 :   // Break down MIMEInfo but don't panic if we can't get all the pieces - we
    1361                 :   // can still download the file
    1362              40 :   nsCAutoString persistentDescriptor, mimeType;
    1363              20 :   nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
    1364              20 :   if (aMIMEInfo) {
    1365               6 :     (void)aMIMEInfo->GetType(mimeType);
    1366                 : 
    1367              12 :     nsCOMPtr<nsIHandlerApp> handlerApp;
    1368               6 :     (void)aMIMEInfo->GetPreferredApplicationHandler(getter_AddRefs(handlerApp));
    1369              12 :     nsCOMPtr<nsILocalHandlerApp> locHandlerApp = do_QueryInterface(handlerApp);
    1370                 : 
    1371               6 :     if (locHandlerApp) {
    1372               0 :       nsCOMPtr<nsIFile> executable;
    1373               0 :       (void)locHandlerApp->GetExecutable(getter_AddRefs(executable));
    1374               0 :       nsCOMPtr<nsILocalFile> locExecutable = do_QueryInterface(executable);
    1375                 : 
    1376               0 :       if (locExecutable)
    1377               0 :         (void)locExecutable->GetPersistentDescriptor(persistentDescriptor);
    1378                 :     }
    1379                 : 
    1380               6 :     (void)aMIMEInfo->GetPreferredAction(&action);
    1381                 :   }
    1382                 : 
    1383              20 :   DownloadState startState = nsIDownloadManager::DOWNLOAD_QUEUED;
    1384                 : 
    1385              20 :   PRInt64 id = AddDownloadToDB(dl->mDisplayName, source, target, tempPath,
    1386              40 :                                dl->mStartTime, dl->mLastUpdate,
    1387              80 :                                mimeType, persistentDescriptor, action);
    1388              20 :   NS_ENSURE_TRUE(id, NS_ERROR_FAILURE);
    1389              20 :   dl->mID = id;
    1390                 : 
    1391              20 :   rv = AddToCurrentDownloads(dl);
    1392              20 :   (void)dl->SetState(startState);
    1393              20 :   NS_ENSURE_SUCCESS(rv, rv);
    1394                 : 
    1395                 : #ifdef DOWNLOAD_SCANNER
    1396                 :   if (mScanner) {
    1397                 :     bool scan = true;
    1398                 :     nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
    1399                 :     if (prefs) {
    1400                 :       (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan);
    1401                 :     }
    1402                 :     // We currently apply local security policy to downloads when we scan
    1403                 :     // via windows all-in-one download security api. The CheckPolicy call
    1404                 :     // below is a pre-emptive part of that process. So tie applying security
    1405                 :     // zone policy settings when downloads are intiated to the same pref
    1406                 :     // that triggers applying security zone policy settings after a download
    1407                 :     // completes. (bug 504804)
    1408                 :     if (scan) {
    1409                 :       AVCheckPolicyState res = mScanner->CheckPolicy(aSource, aTarget);
    1410                 :       if (res == AVPOLICY_BLOCKED) {
    1411                 :         // This download will get deleted during a call to IAE's Save,
    1412                 :         // so go ahead and mark it as blocked and avoid the download.
    1413                 :         (void)CancelDownload(id);
    1414                 :         startState = nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY;
    1415                 :       }
    1416                 :     }
    1417                 :   }
    1418                 : #endif
    1419                 : 
    1420                 :   // Check with parental controls to see if file downloads
    1421                 :   // are allowed for this user. If not allowed, cancel the
    1422                 :   // download and mark its state as being blocked.
    1423                 :   nsCOMPtr<nsIParentalControlsService> pc =
    1424              40 :     do_CreateInstance(NS_PARENTALCONTROLSSERVICE_CONTRACTID);
    1425              20 :   if (pc) {
    1426               0 :     bool enabled = false;
    1427               0 :     (void)pc->GetBlockFileDownloadsEnabled(&enabled);
    1428               0 :     if (enabled) {
    1429               0 :       (void)CancelDownload(id);
    1430               0 :       (void)dl->SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL);
    1431                 :     }
    1432                 : 
    1433                 :     // Log the event if required by pc settings.
    1434               0 :     bool logEnabled = false;
    1435               0 :     (void)pc->GetLoggingEnabled(&logEnabled);
    1436               0 :     if (logEnabled) {
    1437               0 :       (void)pc->Log(nsIParentalControlsService::ePCLog_FileDownload,
    1438                 :                     enabled,
    1439                 :                     aSource,
    1440               0 :                     nsnull);
    1441                 :     }
    1442                 :   }
    1443                 : 
    1444              20 :   NS_ADDREF(*aDownload = dl);
    1445                 : 
    1446              20 :   return NS_OK;
    1447                 : }
    1448                 : 
    1449                 : NS_IMETHODIMP
    1450              21 : nsDownloadManager::GetDownload(PRUint32 aID, nsIDownload **aDownloadItem)
    1451                 : {
    1452              21 :   nsDownload *itm = FindDownload(aID);
    1453                 : 
    1454              42 :   nsRefPtr<nsDownload> dl;
    1455              21 :   if (!itm) {
    1456               8 :     nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
    1457               8 :     NS_ENSURE_SUCCESS(rv, rv);
    1458                 : 
    1459               6 :     itm = dl.get();
    1460                 :   }
    1461                 : 
    1462              19 :   NS_ADDREF(*aDownloadItem = itm);
    1463                 : 
    1464              19 :   return NS_OK;
    1465                 : }
    1466                 : 
    1467                 : nsDownload *
    1468              67 : nsDownloadManager::FindDownload(PRUint32 aID)
    1469                 : {
    1470                 :   // we shouldn't ever have many downloads, so we can loop over them
    1471              88 :   for (PRInt32 i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
    1472              54 :     nsDownload *dl = mCurrentDownloads[i];
    1473                 : 
    1474              54 :     if (dl->mID == aID)
    1475              33 :       return dl;
    1476                 :   }
    1477                 : 
    1478              34 :   return nsnull;
    1479                 : }
    1480                 : 
    1481                 : NS_IMETHODIMP
    1482              11 : nsDownloadManager::CancelDownload(PRUint32 aID)
    1483                 : {
    1484                 :   // We AddRef here so we don't lose access to member variables when we remove
    1485              22 :   nsRefPtr<nsDownload> dl = FindDownload(aID);
    1486                 : 
    1487                 :   // if it's null, someone passed us a bad id.
    1488              11 :   if (!dl)
    1489               0 :     return NS_ERROR_FAILURE;
    1490                 : 
    1491                 :   // Don't cancel if download is already finished
    1492              11 :   if (dl->IsFinished())
    1493               0 :     return NS_OK;
    1494                 : 
    1495                 :   // if the download is fake-paused, we have to resume it so we can cancel it
    1496              11 :   if (dl->IsPaused() && !dl->IsResumable())
    1497               0 :     (void)dl->Resume();
    1498                 : 
    1499                 :   // Have the download cancel its connection
    1500              11 :   (void)dl->Cancel();
    1501                 : 
    1502                 :   // Dump the temp file because we know we don't need the file anymore. The
    1503                 :   // underlying transfer creating the file doesn't delete the file because it
    1504                 :   // can't distinguish between a pause that cancels the transfer or a real
    1505                 :   // cancel.
    1506              11 :   if (dl->mTempFile) {
    1507                 :     bool exists;
    1508               3 :     dl->mTempFile->Exists(&exists);
    1509               3 :     if (exists)
    1510               3 :       dl->mTempFile->Remove(false);
    1511                 :   }
    1512                 : 
    1513              22 :   nsCOMPtr<nsILocalFile> file;
    1514              11 :   if (NS_SUCCEEDED(dl->GetTargetFile(getter_AddRefs(file))))
    1515                 :   {
    1516                 :     bool exists;
    1517              11 :     file->Exists(&exists);
    1518              11 :     if (exists)
    1519               2 :       file->Remove(false);
    1520                 :   }
    1521                 : 
    1522              11 :   nsresult rv = dl->SetState(nsIDownloadManager::DOWNLOAD_CANCELED);
    1523              11 :   NS_ENSURE_SUCCESS(rv, rv);
    1524                 : 
    1525              11 :   return NS_OK;
    1526                 : }
    1527                 : 
    1528                 : NS_IMETHODIMP
    1529               3 : nsDownloadManager::RetryDownload(PRUint32 aID)
    1530                 : {
    1531               6 :   nsRefPtr<nsDownload> dl;
    1532               3 :   nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
    1533               3 :   NS_ENSURE_SUCCESS(rv, rv);
    1534                 : 
    1535                 :   // if our download is not canceled or failed, we should fail
    1536              10 :   if (dl->mDownloadState != nsIDownloadManager::DOWNLOAD_FAILED &&
    1537               2 :       dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL &&
    1538               2 :       dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY &&
    1539               2 :       dl->mDownloadState != nsIDownloadManager::DOWNLOAD_DIRTY &&
    1540               2 :       dl->mDownloadState != nsIDownloadManager::DOWNLOAD_CANCELED)
    1541               0 :     return NS_ERROR_FAILURE;
    1542                 : 
    1543                 :   // If the download has failed and is resumable then we first try resuming it
    1544               2 :   if (dl->mDownloadState == nsIDownloadManager::DOWNLOAD_FAILED && dl->IsResumable()) {
    1545               0 :     rv = dl->Resume();
    1546               0 :     if (NS_SUCCEEDED(rv))
    1547               0 :       return rv;
    1548                 :   }
    1549                 : 
    1550                 :   // reset time and download progress
    1551               2 :   dl->SetStartTime(PR_Now());
    1552               2 :   dl->SetProgressBytes(0, -1);
    1553                 : 
    1554                 :   nsCOMPtr<nsIWebBrowserPersist> wbp =
    1555               4 :     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
    1556               2 :   NS_ENSURE_SUCCESS(rv, rv);
    1557                 : 
    1558               2 :   rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES |
    1559               2 :                             nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
    1560               2 :   NS_ENSURE_SUCCESS(rv, rv);
    1561                 : 
    1562               2 :   rv = AddToCurrentDownloads(dl);
    1563               2 :   NS_ENSURE_SUCCESS(rv, rv);
    1564                 : 
    1565               2 :   rv = dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED);
    1566               2 :   NS_ENSURE_SUCCESS(rv, rv);
    1567                 : 
    1568                 :   // Creates a cycle that will be broken when the download finishes
    1569               2 :   dl->mCancelable = wbp;
    1570               2 :   (void)wbp->SetProgressListener(dl);
    1571                 : 
    1572               2 :   rv = wbp->SaveURI(dl->mSource, nsnull, nsnull, nsnull, nsnull, dl->mTarget);
    1573               2 :   if (NS_FAILED(rv)) {
    1574               0 :     dl->mCancelable = nsnull;
    1575               0 :     (void)wbp->SetProgressListener(nsnull);
    1576               0 :     return rv;
    1577                 :   }
    1578                 : 
    1579               2 :   return NS_OK;
    1580                 : }
    1581                 : 
    1582                 : NS_IMETHODIMP
    1583               6 : nsDownloadManager::RemoveDownload(PRUint32 aID)
    1584                 : {
    1585               6 :   nsDownload *dl = FindDownload(aID);
    1586               6 :   NS_ASSERTION(!dl, "Can't call RemoveDownload on a download in progress!");
    1587               6 :   if (dl)
    1588               0 :     return NS_ERROR_FAILURE;
    1589                 : 
    1590              12 :   nsCOMPtr<mozIStorageStatement> stmt;
    1591              12 :   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    1592                 :     "DELETE FROM moz_downloads "
    1593              12 :     "WHERE id = :id"), getter_AddRefs(stmt));
    1594               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1595                 : 
    1596               6 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID); // unsigned; 64-bit to prevent overflow
    1597               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1598                 : 
    1599               6 :   rv = stmt->Execute();
    1600               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1601                 : 
    1602                 :   nsCOMPtr<nsISupportsPRUint32> id =
    1603              12 :     do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
    1604               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1605               6 :   rv = id->SetData(aID);
    1606               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1607                 : 
    1608                 :   // Notify the UI with the topic and download id
    1609               6 :   return mObserverService->NotifyObservers(id,
    1610                 :                                            "download-manager-remove-download",
    1611               6 :                                            nsnull);
    1612                 : }
    1613                 : 
    1614                 : NS_IMETHODIMP
    1615               6 : nsDownloadManager::RemoveDownloadsByTimeframe(PRInt64 aStartTime,
    1616                 :                                               PRInt64 aEndTime)
    1617                 : {
    1618              12 :   nsCOMPtr<mozIStorageStatement> stmt;
    1619              12 :   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    1620                 :     "DELETE FROM moz_downloads "
    1621                 :     "WHERE startTime >= :startTime "
    1622                 :     "AND startTime <= :endTime "
    1623              12 :     "AND state NOT IN (:downloading, :paused, :queued)"), getter_AddRefs(stmt));
    1624               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1625                 : 
    1626                 :   // Bind the times
    1627               6 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
    1628               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1629               6 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime);
    1630               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1631                 : 
    1632                 :   // Bind the active states
    1633               6 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING);
    1634               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1635               6 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("paused"), nsIDownloadManager::DOWNLOAD_PAUSED);
    1636               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1637               6 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
    1638               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1639                 : 
    1640                 :   // Execute
    1641               6 :   rv = stmt->Execute();
    1642               6 :   NS_ENSURE_SUCCESS(rv, rv);
    1643                 : 
    1644                 :   // Notify the UI with the topic and null subject to indicate "remove multiple"
    1645               6 :   return mObserverService->NotifyObservers(nsnull,
    1646                 :                                            "download-manager-remove-download",
    1647               6 :                                            nsnull);
    1648                 : }
    1649                 : 
    1650                 : NS_IMETHODIMP
    1651              11 : nsDownloadManager::CleanUp()
    1652                 : {
    1653                 :   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
    1654                 :                              nsIDownloadManager::DOWNLOAD_FAILED,
    1655                 :                              nsIDownloadManager::DOWNLOAD_CANCELED,
    1656                 :                              nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
    1657                 :                              nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
    1658              11 :                              nsIDownloadManager::DOWNLOAD_DIRTY };
    1659                 : 
    1660              22 :   nsCOMPtr<mozIStorageStatement> stmt;
    1661              22 :   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    1662                 :     "DELETE FROM moz_downloads "
    1663                 :     "WHERE state = ? "
    1664                 :       "OR state = ? "
    1665                 :       "OR state = ? "
    1666                 :       "OR state = ? "
    1667                 :       "OR state = ? "
    1668              22 :       "OR state = ?"), getter_AddRefs(stmt));
    1669              11 :   NS_ENSURE_SUCCESS(rv, rv);
    1670              77 :   for (PRUint32 i = 0; i < ArrayLength(states); ++i) {
    1671              66 :     rv = stmt->BindInt32ByIndex(i, states[i]);
    1672              66 :     NS_ENSURE_SUCCESS(rv, rv);
    1673                 :   }
    1674                 : 
    1675              11 :   rv = stmt->Execute();
    1676              11 :   NS_ENSURE_SUCCESS(rv, rv);
    1677                 : 
    1678                 :   // Notify the UI with the topic and null subject to indicate "remove multiple"
    1679              11 :   return mObserverService->NotifyObservers(nsnull,
    1680                 :                                            "download-manager-remove-download",
    1681              11 :                                            nsnull);
    1682                 : }
    1683                 : 
    1684                 : NS_IMETHODIMP
    1685               3 : nsDownloadManager::GetCanCleanUp(bool *aResult)
    1686                 : {
    1687                 :   // This method should never return anything but NS_OK for the benefit of
    1688                 :   // unwitting consumers.
    1689                 :   
    1690               3 :   *aResult = false;
    1691                 : 
    1692                 :   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
    1693                 :                              nsIDownloadManager::DOWNLOAD_FAILED,
    1694                 :                              nsIDownloadManager::DOWNLOAD_CANCELED,
    1695                 :                              nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
    1696                 :                              nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
    1697               3 :                              nsIDownloadManager::DOWNLOAD_DIRTY };
    1698                 : 
    1699               6 :   nsCOMPtr<mozIStorageStatement> stmt;
    1700               6 :   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    1701                 :     "SELECT COUNT(*) "
    1702                 :     "FROM moz_downloads "
    1703                 :     "WHERE state = ? "
    1704                 :       "OR state = ? "
    1705                 :       "OR state = ? "
    1706                 :       "OR state = ? "
    1707                 :       "OR state = ? "
    1708               6 :       "OR state = ?"), getter_AddRefs(stmt));
    1709               3 :   NS_ENSURE_SUCCESS(rv, NS_OK);
    1710              21 :   for (PRUint32 i = 0; i < ArrayLength(states); ++i) {
    1711              18 :     rv = stmt->BindInt32ByIndex(i, states[i]);
    1712              18 :     NS_ENSURE_SUCCESS(rv, NS_OK);
    1713                 :   }
    1714                 : 
    1715                 :   bool moreResults; // We don't really care...
    1716               3 :   rv = stmt->ExecuteStep(&moreResults);
    1717               3 :   NS_ENSURE_SUCCESS(rv, NS_OK);
    1718                 : 
    1719                 :   PRInt32 count;
    1720               3 :   rv = stmt->GetInt32(0, &count);
    1721               3 :   NS_ENSURE_SUCCESS(rv, NS_OK);
    1722                 : 
    1723               3 :   if (count > 0)
    1724               1 :     *aResult = true;
    1725                 : 
    1726               3 :   return NS_OK;
    1727                 : }
    1728                 : 
    1729                 : NS_IMETHODIMP
    1730               6 : nsDownloadManager::PauseDownload(PRUint32 aID)
    1731                 : {
    1732               6 :   nsDownload *dl = FindDownload(aID);
    1733               6 :   if (!dl)
    1734               1 :     return NS_ERROR_FAILURE;
    1735                 : 
    1736               5 :   return dl->Pause();
    1737                 : }
    1738                 : 
    1739                 : NS_IMETHODIMP
    1740               5 : nsDownloadManager::ResumeDownload(PRUint32 aID)
    1741                 : {
    1742               5 :   nsDownload *dl = FindDownload(aID);
    1743               5 :   if (!dl)
    1744               1 :     return NS_ERROR_FAILURE;
    1745                 : 
    1746               4 :   return dl->Resume();
    1747                 : }
    1748                 : 
    1749                 : NS_IMETHODIMP
    1750             110 : nsDownloadManager::GetDBConnection(mozIStorageConnection **aDBConn)
    1751                 : {
    1752             110 :   NS_ADDREF(*aDBConn = mDBConn);
    1753                 : 
    1754             110 :   return NS_OK;
    1755                 : }
    1756                 : 
    1757                 : NS_IMETHODIMP
    1758              17 : nsDownloadManager::AddListener(nsIDownloadProgressListener *aListener)
    1759                 : {
    1760              17 :   mListeners.AppendObject(aListener);
    1761                 : 
    1762              17 :   return NS_OK;
    1763                 : }
    1764                 : 
    1765                 : NS_IMETHODIMP
    1766               4 : nsDownloadManager::RemoveListener(nsIDownloadProgressListener *aListener)
    1767                 : {
    1768               4 :   mListeners.RemoveObject(aListener);
    1769                 : 
    1770               4 :   return NS_OK;
    1771                 : }
    1772                 : 
    1773                 : void
    1774              80 : nsDownloadManager::NotifyListenersOnDownloadStateChange(PRInt16 aOldState,
    1775                 :                                                         nsIDownload *aDownload)
    1776                 : {
    1777             167 :   for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i)
    1778              87 :     mListeners[i]->OnDownloadStateChange(aOldState, aDownload);
    1779              80 : }
    1780                 : 
    1781                 : void
    1782               7 : nsDownloadManager::NotifyListenersOnProgressChange(nsIWebProgress *aProgress,
    1783                 :                                                    nsIRequest *aRequest,
    1784                 :                                                    PRInt64 aCurSelfProgress,
    1785                 :                                                    PRInt64 aMaxSelfProgress,
    1786                 :                                                    PRInt64 aCurTotalProgress,
    1787                 :                                                    PRInt64 aMaxTotalProgress,
    1788                 :                                                    nsIDownload *aDownload)
    1789                 : {
    1790              17 :   for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i)
    1791              10 :     mListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
    1792                 :                                     aMaxSelfProgress, aCurTotalProgress,
    1793              10 :                                     aMaxTotalProgress, aDownload);
    1794               7 : }
    1795                 : 
    1796                 : void
    1797              54 : nsDownloadManager::NotifyListenersOnStateChange(nsIWebProgress *aProgress,
    1798                 :                                                 nsIRequest *aRequest,
    1799                 :                                                 PRUint32 aStateFlags,
    1800                 :                                                 nsresult aStatus,
    1801                 :                                                 nsIDownload *aDownload)
    1802                 : {
    1803             118 :   for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i)
    1804              64 :     mListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus,
    1805              64 :                                  aDownload);
    1806              54 : }
    1807                 : 
    1808                 : nsresult
    1809              14 : nsDownloadManager::SwitchDatabaseTypeTo(enum nsDownloadManager::DatabaseType aType)
    1810                 : {
    1811              14 :   if (aType == mDBType)
    1812               2 :     return NS_OK; // no-op
    1813                 : 
    1814              12 :   mDBType = aType;
    1815                 : 
    1816              12 :   (void)PauseAllDownloads(true);
    1817              12 :   (void)RemoveAllDownloads();
    1818                 : 
    1819              12 :   nsresult rv = InitDB();
    1820              12 :   NS_ENSURE_SUCCESS(rv, rv);
    1821                 : 
    1822                 :   // Do things *after* initializing various download manager properties such as
    1823                 :   // restoring downloads to a consistent state
    1824              12 :   rv = RestoreDatabaseState();
    1825              12 :   NS_ENSURE_SUCCESS(rv, rv);
    1826                 : 
    1827              12 :   rv = RestoreActiveDownloads();
    1828              12 :   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
    1829                 : 
    1830              12 :   return rv;
    1831                 : }
    1832                 : 
    1833                 : ////////////////////////////////////////////////////////////////////////////////
    1834                 : //// nsINavHistoryObserver
    1835                 : 
    1836                 : NS_IMETHODIMP
    1837              47 : nsDownloadManager::OnBeginUpdateBatch()
    1838                 : {
    1839                 :   // We already have a transaction, so don't make another
    1840              47 :   if (mHistoryTransaction)
    1841               0 :     return NS_OK;
    1842                 : 
    1843                 :   // Start a transaction that commits when deleted
    1844              94 :   mHistoryTransaction = new mozStorageTransaction(mDBConn, true);
    1845                 : 
    1846              47 :   return NS_OK;
    1847                 : }
    1848                 : 
    1849                 : NS_IMETHODIMP
    1850              47 : nsDownloadManager::OnEndUpdateBatch()
    1851                 : {
    1852                 :   // Get rid of the transaction and cause it to commit
    1853              47 :   mHistoryTransaction = nsnull;
    1854                 : 
    1855              47 :   return NS_OK;
    1856                 : }
    1857                 : 
    1858                 : NS_IMETHODIMP
    1859              21 : nsDownloadManager::OnVisit(nsIURI *aURI, PRInt64 aVisitID, PRTime aTime,
    1860                 :                            PRInt64 aSessionID, PRInt64 aReferringID,
    1861                 :                            PRUint32 aTransitionType, const nsACString& aGUID,
    1862                 :                            PRUint32 *aAdded)
    1863                 : {
    1864              21 :   return NS_OK;
    1865                 : }
    1866                 : 
    1867                 : NS_IMETHODIMP
    1868              19 : nsDownloadManager::OnTitleChanged(nsIURI *aURI,
    1869                 :                                   const nsAString &aPageTitle,
    1870                 :                                   const nsACString &aGUID)
    1871                 : {
    1872              19 :   return NS_OK;
    1873                 : }
    1874                 : 
    1875                 : NS_IMETHODIMP
    1876               2 : nsDownloadManager::OnBeforeDeleteURI(nsIURI *aURI,
    1877                 :                                      const nsACString& aGUID,
    1878                 :                                      PRUint16 aReason)
    1879                 : {
    1880               2 :   return NS_OK;
    1881                 : }
    1882                 : 
    1883                 : NS_IMETHODIMP
    1884               3 : nsDownloadManager::OnDeleteURI(nsIURI *aURI,
    1885                 :                                const nsACString& aGUID,
    1886                 :                                PRUint16 aReason)
    1887                 : {
    1888               3 :   return RemoveDownloadsForURI(aURI);
    1889                 : }
    1890                 : 
    1891                 : NS_IMETHODIMP
    1892               2 : nsDownloadManager::OnClearHistory()
    1893                 : {
    1894               2 :   return CleanUp();
    1895                 : }
    1896                 : 
    1897                 : NS_IMETHODIMP
    1898               0 : nsDownloadManager::OnPageChanged(nsIURI *aURI,
    1899                 :                                  PRUint32 aChangedAttribute,
    1900                 :                                  const nsAString& aNewValue,
    1901                 :                                  const nsACString &aGUID)
    1902                 : {
    1903               0 :   return NS_OK;
    1904                 : }
    1905                 : 
    1906                 : NS_IMETHODIMP
    1907               0 : nsDownloadManager::OnDeleteVisits(nsIURI *aURI, PRTime aVisitTime,
    1908                 :                                   const nsACString& aGUID,
    1909                 :                                   PRUint16 aReason)
    1910                 : {
    1911                 :   // Don't bother removing downloads until the page is removed.
    1912               0 :   return NS_OK;
    1913                 : }
    1914                 : 
    1915                 : ////////////////////////////////////////////////////////////////////////////////
    1916                 : //// nsIObserver
    1917                 : 
    1918                 : NS_IMETHODIMP
    1919             166 : nsDownloadManager::Observe(nsISupports *aSubject,
    1920                 :                            const char *aTopic,
    1921                 :                            const PRUnichar *aData)
    1922                 : {
    1923             166 :   PRInt32 currDownloadCount = mCurrentDownloads.Count();
    1924                 : 
    1925                 :   // If we don't need to cancel all the downloads on quit, only count the ones
    1926                 :   // that aren't resumable.
    1927             166 :   if (GetQuitBehavior() != QUIT_AND_CANCEL)
    1928             180 :     for (PRInt32 i = currDownloadCount - 1; i >= 0; --i)
    1929              14 :       if (mCurrentDownloads[i]->IsResumable())
    1930              11 :         currDownloadCount--;
    1931                 : 
    1932                 :   nsresult rv;
    1933             166 :   if (strcmp(aTopic, "oncancel") == 0) {
    1934               0 :     nsCOMPtr<nsIDownload> dl = do_QueryInterface(aSubject, &rv);
    1935               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1936                 : 
    1937                 :     PRUint32 id;
    1938               0 :     dl->GetId(&id);
    1939               0 :     nsDownload *dl2 = FindDownload(id);
    1940               0 :     if (dl2)
    1941               0 :       return CancelDownload(id);
    1942             166 :   } else if (strcmp(aTopic, "profile-before-change") == 0) {
    1943              34 :     mGetIdsForURIStatement->Finalize();
    1944              34 :     mUpdateDownloadStatement->Finalize();
    1945              68 :     mozilla::DebugOnly<nsresult> rv = mDBConn->Close();
    1946              34 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1947             132 :   } else if (strcmp(aTopic, "quit-application") == 0) {
    1948                 :     // Try to pause all downloads and, if appropriate, mark them as auto-resume
    1949                 :     // unless user has specified that downloads should be canceled
    1950              34 :     enum QuitBehavior behavior = GetQuitBehavior();
    1951              34 :     if (behavior != QUIT_AND_CANCEL)
    1952              34 :       (void)PauseAllDownloads(bool(behavior != QUIT_AND_PAUSE));
    1953                 : 
    1954                 :     // Remove downloads to break cycles and cancel downloads
    1955              34 :     (void)RemoveAllDownloads();
    1956                 : 
    1957                 :    // Now that active downloads have been canceled, remove all completed or
    1958                 :    // aborted downloads if the user's retention policy specifies it.
    1959              34 :     if (GetRetentionBehavior() == 1)
    1960               0 :       CleanUp();
    1961              98 :   } else if (strcmp(aTopic, "quit-application-requested") == 0 &&
    1962                 :              currDownloadCount) {
    1963                 :     nsCOMPtr<nsISupportsPRBool> cancelDownloads =
    1964               0 :       do_QueryInterface(aSubject, &rv);
    1965               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1966                 : #ifndef XP_MACOSX
    1967                 :     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
    1968               0 :                            NS_LITERAL_STRING("quitCancelDownloadsAlertTitle").get(),
    1969               0 :                            NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMultiple").get(),
    1970               0 :                            NS_LITERAL_STRING("quitCancelDownloadsAlertMsg").get(),
    1971               0 :                            NS_LITERAL_STRING("dontQuitButtonWin").get());
    1972                 : #else
    1973                 :     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
    1974                 :                            NS_LITERAL_STRING("quitCancelDownloadsAlertTitle").get(),
    1975                 :                            NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMacMultiple").get(),
    1976                 :                            NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMac").get(),
    1977                 :                            NS_LITERAL_STRING("dontQuitButtonMac").get());
    1978                 : #endif
    1979              98 :   } else if (strcmp(aTopic, "offline-requested") == 0 && currDownloadCount) {
    1980                 :     nsCOMPtr<nsISupportsPRBool> cancelDownloads =
    1981               0 :       do_QueryInterface(aSubject, &rv);
    1982               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1983                 :     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
    1984               0 :                            NS_LITERAL_STRING("offlineCancelDownloadsAlertTitle").get(),
    1985               0 :                            NS_LITERAL_STRING("offlineCancelDownloadsAlertMsgMultiple").get(),
    1986               0 :                            NS_LITERAL_STRING("offlineCancelDownloadsAlertMsg").get(),
    1987               0 :                            NS_LITERAL_STRING("dontGoOfflineButton").get());
    1988                 :   }
    1989              98 :   else if (strcmp(aTopic, NS_IOSERVICE_GOING_OFFLINE_TOPIC) == 0) {
    1990                 :     // Pause all downloads, and mark them to auto-resume.
    1991              35 :     (void)PauseAllDownloads(true);
    1992                 :   }
    1993             196 :   else if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0 &&
    1994             133 :            nsDependentString(aData).EqualsLiteral(NS_IOSERVICE_ONLINE)) {
    1995                 :     // We can now resume all downloads that are supposed to auto-resume.
    1996               1 :     (void)ResumeAllDownloads(false);
    1997                 :   }
    1998              62 :   else if (strcmp(aTopic, "dlmgr-switchdb") == 0) {
    1999               4 :     if (NS_LITERAL_STRING("memory").Equals(aData))
    2000               2 :       return SwitchDatabaseTypeTo(DATABASE_MEMORY);
    2001               2 :     else if (NS_LITERAL_STRING("disk").Equals(aData))
    2002               2 :       return SwitchDatabaseTypeTo(DATABASE_DISK);
    2003                 :   }
    2004              58 :   else if (strcmp(aTopic, "alertclickcallback") == 0) {
    2005                 :     nsCOMPtr<nsIDownloadManagerUI> dmui =
    2006               0 :       do_GetService("@mozilla.org/download-manager-ui;1", &rv);
    2007               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2008               0 :     return dmui->Show(nsnull, 0, nsIDownloadManagerUI::REASON_USER_INTERACTED);
    2009              58 :   } else if (strcmp(aTopic, "sleep_notification") == 0) {
    2010                 :     // Pause downloads if we're sleeping, and mark the downloads as auto-resume
    2011               1 :     (void)PauseAllDownloads(true);
    2012              57 :   } else if (strcmp(aTopic, "wake_notification") == 0) {
    2013               1 :     PRInt32 resumeOnWakeDelay = 10000;
    2014               2 :     nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
    2015               1 :     if (pref)
    2016               1 :       (void)pref->GetIntPref(PREF_BDM_RESUMEONWAKEDELAY, &resumeOnWakeDelay);
    2017                 : 
    2018                 :     // Wait a little bit before trying to resume to avoid resuming when network
    2019                 :     // connections haven't restarted yet
    2020               1 :     mResumeOnWakeTimer = do_CreateInstance("@mozilla.org/timer;1");
    2021               1 :     if (resumeOnWakeDelay >= 0 && mResumeOnWakeTimer) {
    2022               1 :       (void)mResumeOnWakeTimer->InitWithFuncCallback(ResumeOnWakeCallback,
    2023               1 :         this, resumeOnWakeDelay, nsITimer::TYPE_ONE_SHOT);
    2024                 :     }
    2025                 :   }
    2026              56 :   else if (strcmp(aTopic, NS_PRIVATE_BROWSING_REQUEST_TOPIC) == 0) {
    2027              12 :     if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData) &&
    2028                 :         currDownloadCount) {
    2029                 :       nsCOMPtr<nsISupportsPRBool> cancelDownloads =
    2030               4 :         do_QueryInterface(aSubject, &rv);
    2031               2 :       NS_ENSURE_SUCCESS(rv, rv);
    2032                 :       ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
    2033               2 :                              NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertTitle").get(),
    2034               2 :                              NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertMsgMultiple").get(),
    2035               2 :                              NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertMsg").get(),
    2036               6 :                              NS_LITERAL_STRING("dontEnterPrivateBrowsingButton").get());
    2037                 :     }
    2038              16 :     else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData) &&
    2039               6 :              mCurrentDownloads.Count()) {
    2040                 :       nsCOMPtr<nsISupportsPRBool> cancelDownloads =
    2041               4 :         do_QueryInterface(aSubject, &rv);
    2042               2 :       NS_ENSURE_SUCCESS(rv, rv);
    2043                 :       ConfirmCancelDownloads(mCurrentDownloads.Count(), cancelDownloads,
    2044               2 :                              NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertTitle").get(),
    2045               2 :                              NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertMsgMultiple").get(),
    2046               2 :                              NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertMsg").get(),
    2047               6 :                              NS_LITERAL_STRING("dontLeavePrivateBrowsingButton").get());
    2048                 :     }
    2049                 :   }
    2050              44 :   else if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
    2051              10 :     if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData))
    2052               5 :       OnEnterPrivateBrowsingMode();
    2053               5 :     else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData))
    2054               5 :       OnLeavePrivateBrowsingMode();
    2055                 :   }
    2056                 : 
    2057             162 :   return NS_OK;
    2058                 : }
    2059                 : 
    2060                 : void
    2061               5 : nsDownloadManager::OnEnterPrivateBrowsingMode()
    2062                 : {
    2063                 :   // Pause all downloads, and mark them to auto-resume.
    2064               5 :   (void)PauseAllDownloads(true);
    2065                 : 
    2066                 :   // Switch to using an in-memory DB
    2067               5 :   (void)SwitchDatabaseTypeTo(DATABASE_MEMORY);
    2068                 : 
    2069               5 :   mInPrivateBrowsing = true;
    2070               5 : }
    2071                 : 
    2072                 : void
    2073               5 : nsDownloadManager::OnLeavePrivateBrowsingMode()
    2074                 : {
    2075                 :   // We can now resume all downloads that are supposed to auto-resume.
    2076               5 :   (void)ResumeAllDownloads(false);
    2077                 : 
    2078                 :   // Switch back to the on-disk DB again
    2079               5 :   (void)SwitchDatabaseTypeTo(DATABASE_DISK);
    2080                 : 
    2081               5 :   mInPrivateBrowsing = false;
    2082               5 : }
    2083                 : 
    2084                 : void
    2085               4 : nsDownloadManager::ConfirmCancelDownloads(PRInt32 aCount,
    2086                 :                                           nsISupportsPRBool *aCancelDownloads,
    2087                 :                                           const PRUnichar *aTitle,
    2088                 :                                           const PRUnichar *aCancelMessageMultiple,
    2089                 :                                           const PRUnichar *aCancelMessageSingle,
    2090                 :                                           const PRUnichar *aDontCancelButton)
    2091                 : {
    2092                 :   // If user has already dismissed quit request, then do nothing
    2093               4 :   bool quitRequestCancelled = false;
    2094               4 :   aCancelDownloads->GetData(&quitRequestCancelled);
    2095               4 :   if (quitRequestCancelled)
    2096               0 :     return;
    2097                 : 
    2098               8 :   nsXPIDLString title, message, quitButton, dontQuitButton;
    2099                 : 
    2100               4 :   mBundle->GetStringFromName(aTitle, getter_Copies(title));
    2101                 : 
    2102               8 :   nsAutoString countString;
    2103               4 :   countString.AppendInt(aCount);
    2104               4 :   const PRUnichar *strings[1] = { countString.get() };
    2105               4 :   if (aCount > 1) {
    2106               0 :     mBundle->FormatStringFromName(aCancelMessageMultiple, strings, 1,
    2107               0 :                                   getter_Copies(message));
    2108               0 :     mBundle->FormatStringFromName(NS_LITERAL_STRING("cancelDownloadsOKTextMultiple").get(),
    2109               0 :                                   strings, 1, getter_Copies(quitButton));
    2110                 :   } else {
    2111               4 :     mBundle->GetStringFromName(aCancelMessageSingle, getter_Copies(message));
    2112               8 :     mBundle->GetStringFromName(NS_LITERAL_STRING("cancelDownloadsOKText").get(),
    2113               8 :                                getter_Copies(quitButton));
    2114                 :   }
    2115                 : 
    2116               4 :   mBundle->GetStringFromName(aDontCancelButton, getter_Copies(dontQuitButton));
    2117                 : 
    2118                 :   // Get Download Manager window, to be parent of alert.
    2119               8 :   nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
    2120               8 :   nsCOMPtr<nsIDOMWindow> dmWindow;
    2121               4 :   if (wm) {
    2122               8 :     wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(),
    2123               8 :                             getter_AddRefs(dmWindow));
    2124                 :   }
    2125                 : 
    2126                 :   // Show alert.
    2127               8 :   nsCOMPtr<nsIPromptService> prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
    2128               4 :   if (prompter) {
    2129               4 :     PRInt32 flags = (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_0) + (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1);
    2130               4 :     bool nothing = false;
    2131                 :     PRInt32 button;
    2132               4 :     prompter->ConfirmEx(dmWindow, title, message, flags, quitButton.get(), dontQuitButton.get(), nsnull, nsnull, &nothing, &button);
    2133                 : 
    2134               4 :     aCancelDownloads->SetData(button == 1);
    2135                 :   }
    2136                 : }
    2137                 : 
    2138                 : ////////////////////////////////////////////////////////////////////////////////
    2139                 : //// nsDownload
    2140                 : 
    2141                 : NS_IMPL_CLASSINFO(nsDownload, NULL, 0, NS_DOWNLOAD_CID)
    2142            2913 : NS_IMPL_ISUPPORTS4_CI(
    2143                 :     nsDownload
    2144                 :   , nsIDownload
    2145                 :   , nsITransfer
    2146                 :   , nsIWebProgressListener
    2147                 :   , nsIWebProgressListener2
    2148              63 : )
    2149                 : 
    2150              35 : nsDownload::nsDownload() : mDownloadState(nsIDownloadManager::DOWNLOAD_NOTSTARTED),
    2151                 :                            mID(0),
    2152                 :                            mPercentComplete(0),
    2153                 :                            mCurrBytes(0),
    2154                 :                            mMaxBytes(-1),
    2155                 :                            mStartTime(0),
    2156              35 :                            mLastUpdate(PR_Now() - (PRUint32)gUpdateInterval),
    2157                 :                            mResumedAt(-1),
    2158                 :                            mSpeed(0),
    2159                 :                            mHasMultipleFiles(false),
    2160              70 :                            mAutoResume(DONT_RESUME)
    2161                 : {
    2162              35 : }
    2163                 : 
    2164              70 : nsDownload::~nsDownload()
    2165                 : {
    2166             140 : }
    2167                 : 
    2168                 : nsresult
    2169              80 : nsDownload::SetState(DownloadState aState)
    2170                 : {
    2171              80 :   NS_ASSERTION(mDownloadState != aState,
    2172                 :                "Trying to set the download state to what it already is set to!");
    2173                 : 
    2174              80 :   PRInt16 oldState = mDownloadState;
    2175              80 :   mDownloadState = aState;
    2176                 : 
    2177                 :   // We don't want to lose access to our member variables
    2178             160 :   nsRefPtr<nsDownload> kungFuDeathGrip = this;
    2179                 : 
    2180                 :   // When the state changed listener is dispatched, queries to the database and
    2181                 :   // the download manager api should reflect what the nsIDownload object would
    2182                 :   // return. So, if a download is done (finished, canceled, etc.), it should
    2183                 :   // first be removed from the current downloads.  We will also have to update
    2184                 :   // the database *before* notifying listeners.  At this point, you can safely
    2185                 :   // dispatch to the observers as well.
    2186              80 :   switch (aState) {
    2187                 :     case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
    2188                 :     case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
    2189                 :     case nsIDownloadManager::DOWNLOAD_DIRTY:
    2190                 :     case nsIDownloadManager::DOWNLOAD_CANCELED:
    2191                 :     case nsIDownloadManager::DOWNLOAD_FAILED:
    2192                 :       // Transfers are finished, so break the reference cycle
    2193              11 :       Finalize();
    2194              11 :       break;
    2195                 : #ifdef DOWNLOAD_SCANNER
    2196                 :     case nsIDownloadManager::DOWNLOAD_SCANNING:
    2197                 :     {
    2198                 :       nsresult rv = mDownloadManager->mScanner ? mDownloadManager->mScanner->ScanDownload(this) : NS_ERROR_NOT_INITIALIZED;
    2199                 :       // If we failed, then fall through to 'download finished'
    2200                 :       if (NS_SUCCEEDED(rv))
    2201                 :         break;
    2202                 :       mDownloadState = aState = nsIDownloadManager::DOWNLOAD_FINISHED;
    2203                 :     }
    2204                 : #endif
    2205                 :     case nsIDownloadManager::DOWNLOAD_FINISHED:
    2206                 :     {
    2207                 :       // Do what exthandler would have done if necessary
    2208              14 :       nsresult rv = ExecuteDesiredAction();
    2209              14 :       if (NS_FAILED(rv)) {
    2210                 :         // We've failed to execute the desired action.  As a result, we should
    2211                 :         // fail the download so the user can try again.
    2212               0 :         (void)FailDownload(rv, nsnull);
    2213               0 :         return rv;
    2214                 :       }
    2215                 : 
    2216                 :       // Now that we're done with handling the download, clean it up
    2217              14 :       Finalize();
    2218                 : 
    2219              28 :       nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
    2220                 : 
    2221                 :       // Master pref to control this function.
    2222              14 :       bool showTaskbarAlert = true;
    2223              14 :       if (pref)
    2224              14 :         pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert);
    2225                 : 
    2226              14 :       if (showTaskbarAlert) {
    2227               0 :         PRInt32 alertInterval = 2000;
    2228               0 :         if (pref)
    2229               0 :           pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
    2230                 : 
    2231               0 :         PRInt64 alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC;
    2232               0 :         PRInt64 goat = PR_Now() - mStartTime;
    2233               0 :         showTaskbarAlert = goat > alertIntervalUSec;
    2234                 : 
    2235               0 :         PRInt32 size = mDownloadManager->mCurrentDownloads.Count();
    2236               0 :         if (showTaskbarAlert && size == 0) {
    2237                 :           nsCOMPtr<nsIAlertsService> alerts =
    2238               0 :             do_GetService("@mozilla.org/alerts-service;1");
    2239               0 :           if (alerts) {
    2240               0 :               nsXPIDLString title, message;
    2241                 : 
    2242               0 :               mDownloadManager->mBundle->GetStringFromName(
    2243               0 :                   NS_LITERAL_STRING("downloadsCompleteTitle").get(),
    2244               0 :                   getter_Copies(title));
    2245               0 :               mDownloadManager->mBundle->GetStringFromName(
    2246               0 :                   NS_LITERAL_STRING("downloadsCompleteMsg").get(),
    2247               0 :                   getter_Copies(message));
    2248                 : 
    2249                 :               bool removeWhenDone =
    2250               0 :                 mDownloadManager->GetRetentionBehavior() == 0;
    2251                 : 
    2252                 :               // If downloads are automatically removed per the user's
    2253                 :               // retention policy, there's no reason to make the text clickable
    2254                 :               // because if it is, they'll click open the download manager and
    2255                 :               // the items they downloaded will have been removed.
    2256               0 :               alerts->ShowAlertNotification(
    2257               0 :                   NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title,
    2258               0 :                   message, !removeWhenDone, EmptyString(), mDownloadManager,
    2259               0 :                   EmptyString());
    2260                 :             }
    2261                 :         }
    2262                 :       }
    2263                 : 
    2264                 : #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
    2265                 :       nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget);
    2266                 :       nsCOMPtr<nsIFile> file;
    2267                 :       nsAutoString path;
    2268                 : 
    2269                 :       if (fileURL &&
    2270                 :           NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
    2271                 :           file &&
    2272                 :           NS_SUCCEEDED(file->GetPath(path))) {
    2273                 : 
    2274                 : #ifdef XP_WIN
    2275                 :         // On windows, add the download to the system's "recent documents"
    2276                 :         // list, with a pref to disable.
    2277                 :         {
    2278                 :           bool addToRecentDocs = true;
    2279                 :           if (pref)
    2280                 :             pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
    2281                 : 
    2282                 :           if (addToRecentDocs &&
    2283                 :               !nsDownloadManager::gDownloadManagerService->mInPrivateBrowsing) {
    2284                 :             ::SHAddToRecentDocs(SHARD_PATHW, path.get());
    2285                 :           }
    2286                 :         }
    2287                 : #endif
    2288                 : #ifdef XP_MACOSX
    2289                 :         // On OS X, make the downloads stack bounce.
    2290                 :         CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault,
    2291                 :                                                  NS_ConvertUTF16toUTF8(path).get(),
    2292                 :                                                  kCFStringEncodingUTF8);
    2293                 :         CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
    2294                 :         ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
    2295                 :                                                observedObject, NULL, TRUE);
    2296                 :         ::CFRelease(observedObject);
    2297                 : #endif
    2298                 : #ifdef MOZ_WIDGET_ANDROID
    2299                 :         nsCOMPtr<nsIMIMEInfo> mimeInfo;
    2300                 :         nsCAutoString contentType;
    2301                 :         GetMIMEInfo(getter_AddRefs(mimeInfo));
    2302                 : 
    2303                 :         if (mimeInfo)
    2304                 :           mimeInfo->GetMIMEType(contentType);
    2305                 : 
    2306                 :         mozilla::AndroidBridge::Bridge()->ScanMedia(path, contentType);
    2307                 : #endif
    2308                 :       }
    2309                 : 
    2310                 : #ifdef XP_WIN
    2311                 :       // Adjust file attributes so that by default, new files are indexed
    2312                 :       // by desktop search services. Skip off those that land in the temp
    2313                 :       // folder.
    2314                 :       nsCOMPtr<nsIFile> tempDir, fileDir;
    2315                 :       rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempDir));
    2316                 :       NS_ENSURE_SUCCESS(rv, rv);
    2317                 :       (void)file->GetParent(getter_AddRefs(fileDir));
    2318                 : 
    2319                 :       bool isTemp = false;
    2320                 :       if (fileDir)
    2321                 :         (void)fileDir->Equals(tempDir, &isTemp);
    2322                 : 
    2323                 :       nsCOMPtr<nsILocalFileWin> localFileWin(do_QueryInterface(file));
    2324                 :       if (!isTemp && localFileWin)
    2325                 :         (void)localFileWin->SetFileAttributesWin(nsILocalFileWin::WFA_SEARCH_INDEXED);
    2326                 : #endif
    2327                 : 
    2328                 : #endif
    2329                 :       // Now remove the download if the user's retention policy is "Remove when Done"
    2330              14 :       if (mDownloadManager->GetRetentionBehavior() == 0)
    2331               0 :         mDownloadManager->RemoveDownload(mID);
    2332                 :     }
    2333              14 :     break;
    2334                 :   default:
    2335              55 :     break;
    2336                 :   }
    2337                 : 
    2338                 :   // Before notifying the listener, we must update the database so that calls
    2339                 :   // to it work out properly.
    2340              80 :   nsresult rv = UpdateDB();
    2341              80 :   NS_ENSURE_SUCCESS(rv, rv);
    2342                 : 
    2343              80 :   mDownloadManager->NotifyListenersOnDownloadStateChange(oldState, this);
    2344                 : 
    2345              80 :   switch (mDownloadState) {
    2346                 :     case nsIDownloadManager::DOWNLOAD_DOWNLOADING:
    2347                 :       // Only send the dl-start event to downloads that are actually starting.
    2348              18 :       if (oldState == nsIDownloadManager::DOWNLOAD_QUEUED)
    2349              11 :         mDownloadManager->SendEvent(this, "dl-start");
    2350              18 :       break;
    2351                 :     case nsIDownloadManager::DOWNLOAD_FAILED:
    2352               0 :       mDownloadManager->SendEvent(this, "dl-failed");
    2353               0 :       break;
    2354                 :     case nsIDownloadManager::DOWNLOAD_SCANNING:
    2355               0 :       mDownloadManager->SendEvent(this, "dl-scanning");
    2356               0 :       break;
    2357                 :     case nsIDownloadManager::DOWNLOAD_FINISHED:
    2358              14 :       mDownloadManager->SendEvent(this, "dl-done");
    2359              14 :       break;
    2360                 :     case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
    2361                 :     case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
    2362               0 :       mDownloadManager->SendEvent(this, "dl-blocked");
    2363               0 :       break;
    2364                 :     case nsIDownloadManager::DOWNLOAD_DIRTY:
    2365               0 :       mDownloadManager->SendEvent(this, "dl-dirty");
    2366               0 :       break;
    2367                 :     case nsIDownloadManager::DOWNLOAD_CANCELED:
    2368              12 :       mDownloadManager->SendEvent(this, "dl-cancel");
    2369              12 :       break;
    2370                 :     default:
    2371              36 :       break;
    2372                 :   }
    2373              80 :   return NS_OK;
    2374                 : }
    2375                 : 
    2376                 : ////////////////////////////////////////////////////////////////////////////////
    2377                 : //// nsIWebProgressListener2
    2378                 : 
    2379                 : NS_IMETHODIMP
    2380              60 : nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
    2381                 :                                nsIRequest *aRequest,
    2382                 :                                PRInt64 aCurSelfProgress,
    2383                 :                                PRInt64 aMaxSelfProgress,
    2384                 :                                PRInt64 aCurTotalProgress,
    2385                 :                                PRInt64 aMaxTotalProgress)
    2386                 : {
    2387              60 :   if (!mRequest)
    2388              17 :     mRequest = aRequest; // used for pause/resume
    2389                 : 
    2390              60 :   if (mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED) {
    2391                 :     // Obtain the referrer
    2392                 :     nsresult rv;
    2393              34 :     nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
    2394              34 :     nsCOMPtr<nsIURI> referrer = mReferrer;
    2395              17 :     if (channel)
    2396              17 :       (void)NS_GetReferrerFromChannel(channel, getter_AddRefs(mReferrer));
    2397                 : 
    2398                 :     // Restore the original referrer if the new one isn't useful
    2399              17 :     if (!mReferrer)
    2400              17 :       mReferrer = referrer;
    2401                 : 
    2402                 :     // If we have a MIME info, we know that exthandler has already added this to
    2403                 :     // the history, but if we do not, we'll have to add it ourselves.
    2404              17 :     if (!mMIMEInfo) {
    2405                 :       nsCOMPtr<nsIDownloadHistory> dh =
    2406              22 :         do_GetService(NS_DOWNLOADHISTORY_CONTRACTID);
    2407              11 :       if (dh)
    2408              11 :         (void)dh->AddDownload(mSource, mReferrer, mStartTime, mTarget);
    2409                 :     }
    2410                 : 
    2411                 :     // Fetch the entityID, but if we can't get it, don't panic (non-resumable)
    2412              34 :     nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(aRequest));
    2413              17 :     if (resumableChannel)
    2414              17 :       (void)resumableChannel->GetEntityID(mEntityID);
    2415                 : 
    2416                 :     // Before we update the state and dispatch state notifications, we want to
    2417                 :     // ensure that we have the correct state for this download with regards to
    2418                 :     // its percent completion and size.
    2419              17 :     SetProgressBytes(0, aMaxTotalProgress);
    2420                 : 
    2421                 :     // Update the state and the database
    2422              17 :     rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
    2423              17 :     NS_ENSURE_SUCCESS(rv, rv);
    2424                 :   }
    2425                 : 
    2426                 :   // filter notifications since they come in so frequently
    2427              60 :   PRTime now = PR_Now();
    2428              60 :   PRIntervalTime delta = now - mLastUpdate;
    2429              60 :   if (delta < gUpdateInterval)
    2430              53 :     return NS_OK;
    2431                 : 
    2432               7 :   mLastUpdate = now;
    2433                 : 
    2434                 :   // Calculate the speed using the elapsed delta time and bytes downloaded
    2435                 :   // during that time for more accuracy.
    2436               7 :   double elapsedSecs = double(delta) / PR_USEC_PER_SEC;
    2437               7 :   if (elapsedSecs > 0) {
    2438               7 :     double speed = double(aCurTotalProgress - mCurrBytes) / elapsedSecs;
    2439               7 :     if (mCurrBytes == 0) {
    2440               7 :       mSpeed = speed;
    2441                 :     } else {
    2442                 :       // Calculate 'smoothed average' of 10 readings.
    2443               0 :       mSpeed = mSpeed * 0.9 + speed * 0.1;
    2444                 :     }
    2445                 :   }
    2446                 : 
    2447               7 :   SetProgressBytes(aCurTotalProgress, aMaxTotalProgress);
    2448                 : 
    2449                 :   // Report to the listener our real sizes
    2450                 :   PRInt64 currBytes, maxBytes;
    2451               7 :   (void)GetAmountTransferred(&currBytes);
    2452               7 :   (void)GetSize(&maxBytes);
    2453                 :   mDownloadManager->NotifyListenersOnProgressChange(
    2454               7 :     aWebProgress, aRequest, currBytes, maxBytes, currBytes, maxBytes, this);
    2455                 : 
    2456                 :   // If the maximums are different, then there must be more than one file
    2457               7 :   if (aMaxSelfProgress != aMaxTotalProgress)
    2458               0 :     mHasMultipleFiles = true;
    2459                 : 
    2460               7 :   return NS_OK;
    2461                 : }
    2462                 : 
    2463                 : NS_IMETHODIMP
    2464               0 : nsDownload::OnRefreshAttempted(nsIWebProgress *aWebProgress,
    2465                 :                                nsIURI *aUri,
    2466                 :                                PRInt32 aDelay,
    2467                 :                                bool aSameUri,
    2468                 :                                bool *allowRefresh)
    2469                 : {
    2470               0 :   *allowRefresh = true;
    2471               0 :   return NS_OK;
    2472                 : }
    2473                 : 
    2474                 : ////////////////////////////////////////////////////////////////////////////////
    2475                 : //// nsIWebProgressListener
    2476                 : 
    2477                 : NS_IMETHODIMP
    2478               0 : nsDownload::OnProgressChange(nsIWebProgress *aWebProgress,
    2479                 :                              nsIRequest *aRequest,
    2480                 :                              PRInt32 aCurSelfProgress,
    2481                 :                              PRInt32 aMaxSelfProgress,
    2482                 :                              PRInt32 aCurTotalProgress,
    2483                 :                              PRInt32 aMaxTotalProgress)
    2484                 : {
    2485                 :   return OnProgressChange64(aWebProgress, aRequest,
    2486                 :                             aCurSelfProgress, aMaxSelfProgress,
    2487               0 :                             aCurTotalProgress, aMaxTotalProgress);
    2488                 : }
    2489                 : 
    2490                 : NS_IMETHODIMP
    2491               0 : nsDownload::OnLocationChange(nsIWebProgress *aWebProgress,
    2492                 :                              nsIRequest *aRequest, nsIURI *aLocation,
    2493                 :                              PRUint32 aFlags)
    2494                 : {
    2495               0 :   return NS_OK;
    2496                 : }
    2497                 : 
    2498                 : NS_IMETHODIMP
    2499               0 : nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
    2500                 :                            nsIRequest *aRequest, nsresult aStatus,
    2501                 :                            const PRUnichar *aMessage)
    2502                 : {
    2503               0 :   if (NS_FAILED(aStatus))
    2504               0 :     return FailDownload(aStatus, aMessage);
    2505               0 :   return NS_OK;
    2506                 : }
    2507                 : 
    2508                 : NS_IMETHODIMP
    2509              54 : nsDownload::OnStateChange(nsIWebProgress *aWebProgress,
    2510                 :                           nsIRequest *aRequest, PRUint32 aStateFlags,
    2511                 :                           nsresult aStatus)
    2512                 : {
    2513                 :   // We don't want to lose access to our member variables
    2514             108 :   nsRefPtr<nsDownload> kungFuDeathGrip = this;
    2515                 : 
    2516                 :   // Check if we're starting a request; the NETWORK flag is necessary to not
    2517                 :   // pick up the START of *each* file but only for the whole request
    2518              54 :   if ((aStateFlags & STATE_START) && (aStateFlags & STATE_IS_NETWORK)) {
    2519                 :     nsresult rv;
    2520              58 :     nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
    2521              29 :     if (NS_SUCCEEDED(rv)) {
    2522                 :       PRUint32 status;
    2523              26 :       rv = channel->GetResponseStatus(&status);
    2524                 :       // HTTP 450 - Blocked by parental control proxies
    2525              26 :       if (NS_SUCCEEDED(rv) && status == 450) {
    2526                 :         // Cancel using the provided object
    2527               0 :         (void)Cancel();
    2528                 : 
    2529                 :         // Fail the download
    2530               0 :         (void)SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL);
    2531                 :       }
    2532              29 :     }
    2533              39 :   } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK) &&
    2534              14 :              IsFinishable()) {
    2535                 :     // We got both STOP and NETWORK so that means the whole request is done
    2536                 :     // (and not just a single file if there are multiple files)
    2537              14 :     if (NS_SUCCEEDED(aStatus)) {
    2538                 :       // We can't completely trust the bytes we've added up because we might be
    2539                 :       // missing on some/all of the progress updates (especially from cache).
    2540                 :       // Our best bet is the file itself, but if for some reason it's gone or
    2541                 :       // if we have multiple files, the next best is what we've calculated.
    2542                 :       PRInt64 fileSize;
    2543              28 :       nsCOMPtr<nsILocalFile> file;
    2544                 :       //  We need a nsIFile clone to deal with file size caching issues. :(
    2545              28 :       nsCOMPtr<nsIFile> clone;
    2546              98 :       if (!mHasMultipleFiles &&
    2547              42 :           NS_SUCCEEDED(GetTargetFile(getter_AddRefs(file))) &&
    2548              42 :           NS_SUCCEEDED(file->Clone(getter_AddRefs(clone))) &&
    2549              14 :           NS_SUCCEEDED(clone->GetFileSize(&fileSize)) && fileSize > 0) {
    2550              13 :         mCurrBytes = mMaxBytes = fileSize;
    2551                 : 
    2552                 :         // If we resumed, keep the fact that we did and fix size calculations
    2553              13 :         if (WasResumed())
    2554               4 :           mResumedAt = 0;
    2555               1 :       } else if (mMaxBytes == -1) {
    2556               0 :         mMaxBytes = mCurrBytes;
    2557                 :       } else {
    2558               1 :         mCurrBytes = mMaxBytes;
    2559                 :       }
    2560                 : 
    2561              14 :       mPercentComplete = 100;
    2562              14 :       mLastUpdate = PR_Now();
    2563                 : 
    2564                 : #ifdef DOWNLOAD_SCANNER
    2565                 :       bool scan = true;
    2566                 :       nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
    2567                 :       if (prefs)
    2568                 :         (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan);
    2569                 : 
    2570                 :       if (scan)
    2571                 :         (void)SetState(nsIDownloadManager::DOWNLOAD_SCANNING);
    2572                 :       else
    2573                 :         (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
    2574                 : #else
    2575              14 :       (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
    2576                 : #endif
    2577                 :     } else {
    2578                 :       // We failed for some unknown reason -- fail with a generic message
    2579               0 :       (void)FailDownload(aStatus, nsnull);
    2580                 :     }
    2581                 :   }
    2582                 : 
    2583                 :   mDownloadManager->NotifyListenersOnStateChange(aWebProgress, aRequest,
    2584              54 :                                                  aStateFlags, aStatus, this);
    2585              54 :   return NS_OK;
    2586                 : }
    2587                 : 
    2588                 : NS_IMETHODIMP
    2589               0 : nsDownload::OnSecurityChange(nsIWebProgress *aWebProgress,
    2590                 :                              nsIRequest *aRequest, PRUint32 aState)
    2591                 : {
    2592               0 :   return NS_OK;
    2593                 : }
    2594                 : 
    2595                 : ////////////////////////////////////////////////////////////////////////////////
    2596                 : //// nsIDownload
    2597                 : 
    2598                 : NS_IMETHODIMP
    2599               0 : nsDownload::Init(nsIURI *aSource,
    2600                 :                  nsIURI *aTarget,
    2601                 :                  const nsAString& aDisplayName,
    2602                 :                  nsIMIMEInfo *aMIMEInfo,
    2603                 :                  PRTime aStartTime,
    2604                 :                  nsILocalFile *aTempFile,
    2605                 :                  nsICancelable *aCancelable)
    2606                 : {
    2607               0 :   NS_WARNING("Huh...how did we get here?!");
    2608               0 :   return NS_OK;
    2609                 : }
    2610                 : 
    2611                 : NS_IMETHODIMP
    2612             214 : nsDownload::GetState(PRInt16 *aState)
    2613                 : {
    2614             214 :   *aState = mDownloadState;
    2615             214 :   return NS_OK;
    2616                 : }
    2617                 : 
    2618                 : NS_IMETHODIMP
    2619              11 : nsDownload::GetDisplayName(nsAString &aDisplayName)
    2620                 : {
    2621              11 :   aDisplayName = mDisplayName;
    2622              11 :   return NS_OK;
    2623                 : }
    2624                 : 
    2625                 : NS_IMETHODIMP
    2626               0 : nsDownload::GetCancelable(nsICancelable **aCancelable)
    2627                 : {
    2628               0 :   *aCancelable = mCancelable;
    2629               0 :   NS_IF_ADDREF(*aCancelable);
    2630               0 :   return NS_OK;
    2631                 : }
    2632                 : 
    2633                 : NS_IMETHODIMP
    2634               0 : nsDownload::GetTarget(nsIURI **aTarget)
    2635                 : {
    2636               0 :   *aTarget = mTarget;
    2637               0 :   NS_IF_ADDREF(*aTarget);
    2638               0 :   return NS_OK;
    2639                 : }
    2640                 : 
    2641                 : NS_IMETHODIMP
    2642              55 : nsDownload::GetSource(nsIURI **aSource)
    2643                 : {
    2644              55 :   *aSource = mSource;
    2645              55 :   NS_IF_ADDREF(*aSource);
    2646              55 :   return NS_OK;
    2647                 : }
    2648                 : 
    2649                 : NS_IMETHODIMP
    2650               0 : nsDownload::GetStartTime(PRInt64 *aStartTime)
    2651                 : {
    2652               0 :   *aStartTime = mStartTime;
    2653               0 :   return NS_OK;
    2654                 : }
    2655                 : 
    2656                 : NS_IMETHODIMP
    2657               0 : nsDownload::GetPercentComplete(PRInt32 *aPercentComplete)
    2658                 : {
    2659               0 :   *aPercentComplete = mPercentComplete;
    2660               0 :   return NS_OK;
    2661                 : }
    2662                 : 
    2663                 : NS_IMETHODIMP
    2664             136 : nsDownload::GetAmountTransferred(PRInt64 *aAmountTransferred)
    2665                 : {
    2666             136 :   *aAmountTransferred = mCurrBytes + (WasResumed() ? mResumedAt : 0);
    2667             136 :   return NS_OK;
    2668                 : }
    2669                 : 
    2670                 : NS_IMETHODIMP
    2671             143 : nsDownload::GetSize(PRInt64 *aSize)
    2672                 : {
    2673             143 :   *aSize = mMaxBytes + (WasResumed() && mMaxBytes != -1 ? mResumedAt : 0);
    2674             143 :   return NS_OK;
    2675                 : }
    2676                 : 
    2677                 : NS_IMETHODIMP
    2678               0 : nsDownload::GetMIMEInfo(nsIMIMEInfo **aMIMEInfo)
    2679                 : {
    2680               0 :   *aMIMEInfo = mMIMEInfo;
    2681               0 :   NS_IF_ADDREF(*aMIMEInfo);
    2682               0 :   return NS_OK;
    2683                 : }
    2684                 : 
    2685                 : NS_IMETHODIMP
    2686              97 : nsDownload::GetTargetFile(nsILocalFile **aTargetFile)
    2687                 : {
    2688                 :   nsresult rv;
    2689                 : 
    2690             194 :   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
    2691              97 :   if (NS_FAILED(rv)) return rv;
    2692                 : 
    2693             194 :   nsCOMPtr<nsIFile> file;
    2694              97 :   rv = fileURL->GetFile(getter_AddRefs(file));
    2695              97 :   if (NS_SUCCEEDED(rv))
    2696              97 :     rv = CallQueryInterface(file, aTargetFile);
    2697              97 :   return rv;
    2698                 : }
    2699                 : 
    2700                 : NS_IMETHODIMP
    2701               0 : nsDownload::GetSpeed(double *aSpeed)
    2702                 : {
    2703               0 :   *aSpeed = mSpeed;
    2704               0 :   return NS_OK;
    2705                 : }
    2706                 : 
    2707                 : NS_IMETHODIMP
    2708              57 : nsDownload::GetId(PRUint32 *aId)
    2709                 : {
    2710              57 :   *aId = mID;
    2711              57 :   return NS_OK;
    2712                 : }
    2713                 : 
    2714                 : NS_IMETHODIMP
    2715               2 : nsDownload::GetReferrer(nsIURI **referrer)
    2716                 : {
    2717               2 :   NS_IF_ADDREF(*referrer = mReferrer);
    2718               2 :   return NS_OK;
    2719                 : }
    2720                 : 
    2721                 : NS_IMETHODIMP
    2722               4 : nsDownload::GetResumable(bool *resumable)
    2723                 : {
    2724               4 :   *resumable = IsResumable();
    2725               4 :   return NS_OK;
    2726                 : }
    2727                 : 
    2728                 : ////////////////////////////////////////////////////////////////////////////////
    2729                 : //// nsDownload Helper Functions
    2730                 : 
    2731                 : void
    2732              25 : nsDownload::Finalize()
    2733                 : {
    2734                 :   // We're stopping, so break the cycle we created at download start
    2735              25 :   mCancelable = nsnull;
    2736                 : 
    2737                 :   // Reset values that aren't needed anymore, so the DB can be updated as well
    2738              25 :   mEntityID.Truncate();
    2739              25 :   mTempFile = nsnull;
    2740                 : 
    2741                 :   // Remove ourself from the active downloads
    2742              25 :   (void)mDownloadManager->mCurrentDownloads.RemoveObject(this);
    2743                 : 
    2744                 :   // Make sure we do not automatically resume
    2745              25 :   mAutoResume = DONT_RESUME;
    2746              25 : }
    2747                 : 
    2748                 : nsresult
    2749              14 : nsDownload::ExecuteDesiredAction()
    2750                 : {
    2751                 :   // If we have a temp file and we have resumed, we have to do what the
    2752                 :   // external helper app service would have done.
    2753              14 :   if (!mTempFile || !WasResumed())
    2754              13 :     return NS_OK;
    2755                 : 
    2756                 :   // We need to bail if for some reason the temp file got removed
    2757                 :   bool fileExists;
    2758               1 :   if (NS_FAILED(mTempFile->Exists(&fileExists)) || !fileExists)
    2759               0 :     return NS_ERROR_FILE_NOT_FOUND;
    2760                 : 
    2761                 :   // Assume an unknown action is save to disk
    2762               1 :   nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
    2763               1 :   if (mMIMEInfo) {
    2764               1 :     nsresult rv = mMIMEInfo->GetPreferredAction(&action);
    2765               1 :     NS_ENSURE_SUCCESS(rv, rv);
    2766                 :   }
    2767                 : 
    2768               1 :   nsresult retVal = NS_OK;
    2769               1 :   switch (action) {
    2770                 :     case nsIMIMEInfo::saveToDisk:
    2771                 :       // Move the file to the proper location
    2772               1 :       retVal = MoveTempToTarget();
    2773               1 :       break;
    2774                 :     case nsIMIMEInfo::useHelperApp:
    2775                 :     case nsIMIMEInfo::useSystemDefault:
    2776                 :       // For these cases we have to move the file to the target location and
    2777                 :       // open with the appropriate application
    2778               0 :       retVal = OpenWithApplication();
    2779               0 :       break;
    2780                 :     default:
    2781               0 :       break;
    2782                 :   }
    2783                 : 
    2784               1 :   return retVal;
    2785                 : }
    2786                 : 
    2787                 : nsresult
    2788               1 : nsDownload::MoveTempToTarget()
    2789                 : {
    2790               2 :   nsCOMPtr<nsILocalFile> target;
    2791               1 :   nsresult rv = GetTargetFile(getter_AddRefs(target));
    2792               1 :   NS_ENSURE_SUCCESS(rv, rv);
    2793                 : 
    2794                 :   // MoveTo will fail if the file already exists, but we've already obtained
    2795                 :   // confirmation from the user that this is OK, so remove it if it exists.
    2796                 :   bool fileExists;
    2797               1 :   if (NS_SUCCEEDED(target->Exists(&fileExists)) && fileExists) {
    2798               0 :     rv = target->Remove(false);
    2799               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2800                 :   }
    2801                 : 
    2802                 :   // Extract the new leaf name from the file location
    2803               2 :   nsAutoString fileName;
    2804               1 :   rv = target->GetLeafName(fileName);
    2805               1 :   NS_ENSURE_SUCCESS(rv, rv);
    2806               2 :   nsCOMPtr<nsIFile> dir;
    2807               1 :   rv = target->GetParent(getter_AddRefs(dir));
    2808               1 :   NS_ENSURE_SUCCESS(rv, rv);
    2809               1 :   rv = mTempFile->MoveTo(dir, fileName);
    2810               1 :   NS_ENSURE_SUCCESS(rv, rv);
    2811                 : 
    2812               1 :   return NS_OK;
    2813                 : }
    2814                 : 
    2815                 : nsresult
    2816               0 : nsDownload::OpenWithApplication()
    2817                 : {
    2818                 :   // First move the temporary file to the target location
    2819               0 :   nsCOMPtr<nsILocalFile> target;
    2820               0 :   nsresult rv = GetTargetFile(getter_AddRefs(target));
    2821               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2822                 : 
    2823                 :   // Move the temporary file to the target location
    2824               0 :   rv = MoveTempToTarget();
    2825               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2826                 : 
    2827                 :   // We do not verify the return value here because, irrespective of success
    2828                 :   // or failure of the method, the deletion of temp file has to take place, as
    2829                 :   // per the corresponding preference. But we store this separately as this is
    2830                 :   // what we ultimately return from this function.
    2831               0 :   nsresult retVal = mMIMEInfo->LaunchWithFile(target);
    2832                 : 
    2833                 :   bool deleteTempFileOnExit;
    2834               0 :   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
    2835               0 :   if (!prefs || NS_FAILED(prefs->GetBoolPref(PREF_BH_DELETETEMPFILEONEXIT,
    2836                 :                                              &deleteTempFileOnExit))) {
    2837                 :     // No prefservice or no pref set; use default value
    2838                 : #if !defined(XP_MACOSX)
    2839                 :     // Mac users have been very verbal about temp files being deleted on
    2840                 :     // app exit - they don't like it - but we'll continue to do this on
    2841                 :     // other platforms for now.
    2842               0 :     deleteTempFileOnExit = true;
    2843                 : #else
    2844                 :     deleteTempFileOnExit = false;
    2845                 : #endif
    2846                 :   }
    2847                 : 
    2848                 :   // Always schedule files to be deleted at the end of the private browsing
    2849                 :   // mode, regardless of the value of the pref.
    2850               0 :   if (deleteTempFileOnExit ||
    2851                 :       nsDownloadManager::gDownloadManagerService->mInPrivateBrowsing) {
    2852                 :     // Use the ExternalHelperAppService to push the temporary file to the list
    2853                 :     // of files to be deleted on exit.
    2854                 :     nsCOMPtr<nsPIExternalAppLauncher> appLauncher(do_GetService
    2855               0 :                     (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID));
    2856                 : 
    2857                 :     // Even if we are unable to get this service we return the result
    2858                 :     // of LaunchWithFile() which makes more sense.
    2859               0 :     if (appLauncher)
    2860               0 :       (void)appLauncher->DeleteTemporaryFileOnExit(target);
    2861                 :   }
    2862                 : 
    2863               0 :   return retVal;
    2864                 : }
    2865                 : 
    2866                 : void
    2867              22 : nsDownload::SetStartTime(PRInt64 aStartTime)
    2868                 : {
    2869              22 :   mStartTime = aStartTime;
    2870              22 :   mLastUpdate = aStartTime;
    2871              22 : }
    2872                 : 
    2873                 : void
    2874              48 : nsDownload::SetProgressBytes(PRInt64 aCurrBytes, PRInt64 aMaxBytes)
    2875                 : {
    2876              48 :   mCurrBytes = aCurrBytes;
    2877              48 :   mMaxBytes = aMaxBytes;
    2878                 : 
    2879                 :   // Get the real bytes that include resume position
    2880                 :   PRInt64 currBytes, maxBytes;
    2881              48 :   (void)GetAmountTransferred(&currBytes);
    2882              48 :   (void)GetSize(&maxBytes);
    2883                 : 
    2884              48 :   if (currBytes == maxBytes)
    2885              11 :     mPercentComplete = 100;
    2886              37 :   else if (maxBytes <= 0)
    2887              11 :     mPercentComplete = -1;
    2888                 :   else
    2889              26 :     mPercentComplete = (PRInt32)((PRFloat64)currBytes / maxBytes * 100 + .5);
    2890              48 : }
    2891                 : 
    2892                 : nsresult
    2893              11 : nsDownload::Pause()
    2894                 : {
    2895              11 :   if (!IsResumable())
    2896               2 :     return NS_ERROR_UNEXPECTED;
    2897                 : 
    2898               9 :   nsresult rv = Cancel();
    2899               9 :   NS_ENSURE_SUCCESS(rv, rv);
    2900                 : 
    2901               9 :   return SetState(nsIDownloadManager::DOWNLOAD_PAUSED);
    2902                 : }
    2903                 : 
    2904                 : nsresult
    2905              20 : nsDownload::Cancel()
    2906                 : {
    2907              20 :   nsresult rv = NS_OK;
    2908              20 :   if (mCancelable) {
    2909              15 :     rv = mCancelable->Cancel(NS_BINDING_ABORTED);
    2910                 :     // we're done with this, so break the cycle
    2911              15 :     mCancelable = nsnull;
    2912                 :   }
    2913                 : 
    2914              20 :   return rv;
    2915                 : }
    2916                 : 
    2917                 : nsresult
    2918               7 : nsDownload::Resume()
    2919                 : {
    2920               7 :   if (!IsPaused() || !IsResumable())
    2921               0 :     return NS_ERROR_UNEXPECTED;
    2922                 : 
    2923                 :   nsresult rv;
    2924                 :   nsCOMPtr<nsIWebBrowserPersist> wbp =
    2925              14 :     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
    2926               7 :   NS_ENSURE_SUCCESS(rv, rv);
    2927                 : 
    2928               7 :   rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE |
    2929               7 :                             nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
    2930               7 :   NS_ENSURE_SUCCESS(rv, rv);
    2931                 : 
    2932                 :   // Create a new channel for the source URI
    2933              14 :   nsCOMPtr<nsIChannel> channel;
    2934              14 :   nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(wbp));
    2935               7 :   rv = NS_NewChannel(getter_AddRefs(channel), mSource, nsnull, nsnull, ir);
    2936               7 :   NS_ENSURE_SUCCESS(rv, rv);
    2937                 : 
    2938                 :   // Make sure we can get a file, either the temporary or the real target, for
    2939                 :   // both purposes of file size and a target to write to
    2940              14 :   nsCOMPtr<nsILocalFile> targetLocalFile(mTempFile);
    2941               7 :   if (!targetLocalFile) {
    2942               5 :     rv = GetTargetFile(getter_AddRefs(targetLocalFile));
    2943               5 :     NS_ENSURE_SUCCESS(rv, rv);
    2944                 :   }
    2945                 : 
    2946                 :   // Get the file size to be used as an offset, but if anything goes wrong
    2947                 :   // along the way, we'll silently restart at 0.
    2948                 :   PRInt64 fileSize;
    2949                 :   //  We need a nsIFile clone to deal with file size caching issues. :(
    2950              14 :   nsCOMPtr<nsIFile> clone;
    2951              14 :   if (NS_FAILED(targetLocalFile->Clone(getter_AddRefs(clone))) ||
    2952               7 :       NS_FAILED(clone->GetFileSize(&fileSize)))
    2953               4 :     fileSize = 0;
    2954                 : 
    2955                 :   // Set the channel to resume at the right position along with the entityID
    2956              14 :   nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(channel));
    2957               7 :   if (!resumableChannel)
    2958               0 :     return NS_ERROR_UNEXPECTED;
    2959               7 :   rv = resumableChannel->ResumeAt(fileSize, mEntityID);
    2960               7 :   NS_ENSURE_SUCCESS(rv, rv);
    2961                 : 
    2962                 :   // If we know the max size, we know what it should be when resuming
    2963                 :   PRInt64 maxBytes;
    2964               7 :   GetSize(&maxBytes);
    2965               7 :   SetProgressBytes(0, maxBytes != -1 ? maxBytes - fileSize : -1);
    2966                 :   // Track where we resumed because progress notifications restart at 0
    2967               7 :   mResumedAt = fileSize;
    2968                 : 
    2969                 :   // Set the referrer
    2970               7 :   if (mReferrer) {
    2971               0 :     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
    2972               0 :     if (httpChannel) {
    2973               0 :       rv = httpChannel->SetReferrer(mReferrer);
    2974               0 :       NS_ENSURE_SUCCESS(rv, rv);
    2975                 :     }
    2976                 :   }
    2977                 : 
    2978                 :   // Creates a cycle that will be broken when the download finishes
    2979               7 :   mCancelable = wbp;
    2980               7 :   (void)wbp->SetProgressListener(this);
    2981                 : 
    2982                 :   // Save the channel using nsIWBP
    2983               7 :   rv = wbp->SaveChannel(channel, targetLocalFile);
    2984               7 :   if (NS_FAILED(rv)) {
    2985               0 :     mCancelable = nsnull;
    2986               0 :     (void)wbp->SetProgressListener(nsnull);
    2987               0 :     return rv;
    2988                 :   }
    2989                 : 
    2990               7 :   return SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
    2991                 : }
    2992                 : 
    2993                 : bool
    2994              32 : nsDownload::IsPaused()
    2995                 : {
    2996              32 :   return mDownloadState == nsIDownloadManager::DOWNLOAD_PAUSED;
    2997                 : }
    2998                 : 
    2999                 : bool
    3000              41 : nsDownload::IsResumable()
    3001                 : {
    3002              41 :   return !mEntityID.IsEmpty();
    3003                 : }
    3004                 : 
    3005                 : bool
    3006             295 : nsDownload::WasResumed()
    3007                 : {
    3008             295 :   return mResumedAt != -1;
    3009                 : }
    3010                 : 
    3011                 : bool
    3012              10 : nsDownload::ShouldAutoResume()
    3013                 : {
    3014              10 :   return mAutoResume == AUTO_RESUME;
    3015                 : }
    3016                 : 
    3017                 : bool
    3018              14 : nsDownload::IsFinishable()
    3019                 : {
    3020                 :   return mDownloadState == nsIDownloadManager::DOWNLOAD_NOTSTARTED ||
    3021                 :          mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED ||
    3022              14 :          mDownloadState == nsIDownloadManager::DOWNLOAD_DOWNLOADING;
    3023                 : }
    3024                 : 
    3025                 : bool
    3026              11 : nsDownload::IsFinished()
    3027                 : {
    3028              11 :   return mDownloadState == nsIDownloadManager::DOWNLOAD_FINISHED;
    3029                 : }
    3030                 : 
    3031                 : nsresult
    3032              80 : nsDownload::UpdateDB()
    3033                 : {
    3034              80 :   NS_ASSERTION(mID, "Download ID is stored as zero.  This is bad!");
    3035              80 :   NS_ASSERTION(mDownloadManager, "Egads!  We have no download manager!");
    3036                 : 
    3037              80 :   mozIStorageStatement *stmt = mDownloadManager->mUpdateDownloadStatement;
    3038                 : 
    3039             160 :   nsAutoString tempPath;
    3040              80 :   if (mTempFile)
    3041              17 :     (void)mTempFile->GetPath(tempPath);
    3042              80 :   nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), tempPath);
    3043                 : 
    3044              80 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), mStartTime);
    3045              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3046                 : 
    3047              80 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), mLastUpdate);
    3048              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3049                 : 
    3050              80 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), mDownloadState);
    3051              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3052                 : 
    3053              80 :   if (mReferrer) {
    3054               6 :     nsCAutoString referrer;
    3055               3 :     rv = mReferrer->GetSpec(referrer);
    3056               3 :     NS_ENSURE_SUCCESS(rv, rv);
    3057               6 :     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("referrer"), referrer);
    3058                 :   } else {
    3059              77 :     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("referrer"));
    3060                 :   }
    3061              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3062                 : 
    3063              80 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("entityID"), mEntityID);
    3064              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3065                 : 
    3066                 :   PRInt64 currBytes;
    3067              80 :   (void)GetAmountTransferred(&currBytes);
    3068              80 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("currBytes"), currBytes);
    3069              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3070                 : 
    3071                 :   PRInt64 maxBytes;
    3072              80 :   (void)GetSize(&maxBytes);
    3073              80 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("maxBytes"), maxBytes);
    3074              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3075                 : 
    3076              80 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), mAutoResume);
    3077              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3078                 : 
    3079              80 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mID);
    3080              80 :   NS_ENSURE_SUCCESS(rv, rv);
    3081                 : 
    3082              80 :   return stmt->Execute();
    3083                 : }
    3084                 : 
    3085                 : nsresult
    3086               0 : nsDownload::FailDownload(nsresult aStatus, const PRUnichar *aMessage)
    3087                 : {
    3088                 :   // Grab the bundle before potentially losing our member variables
    3089               0 :   nsCOMPtr<nsIStringBundle> bundle = mDownloadManager->mBundle;
    3090                 : 
    3091               0 :   (void)SetState(nsIDownloadManager::DOWNLOAD_FAILED);
    3092                 : 
    3093                 :   // Get title for alert.
    3094               0 :   nsXPIDLString title;
    3095               0 :   nsresult rv = bundle->GetStringFromName(
    3096               0 :     NS_LITERAL_STRING("downloadErrorAlertTitle").get(), getter_Copies(title));
    3097               0 :   NS_ENSURE_SUCCESS(rv, rv);
    3098                 : 
    3099                 :   // Get a generic message if we weren't supplied one
    3100               0 :   nsXPIDLString message;
    3101               0 :   message = aMessage;
    3102               0 :   if (message.IsEmpty()) {
    3103               0 :     rv = bundle->GetStringFromName(
    3104               0 :       NS_LITERAL_STRING("downloadErrorGeneric").get(), getter_Copies(message));
    3105               0 :     NS_ENSURE_SUCCESS(rv, rv);
    3106                 :   }
    3107                 : 
    3108                 :   // Get Download Manager window to be parent of alert
    3109                 :   nsCOMPtr<nsIWindowMediator> wm =
    3110               0 :     do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
    3111               0 :   NS_ENSURE_SUCCESS(rv, rv);
    3112               0 :   nsCOMPtr<nsIDOMWindow> dmWindow;
    3113               0 :   rv = wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(),
    3114               0 :                                getter_AddRefs(dmWindow));
    3115               0 :   NS_ENSURE_SUCCESS(rv, rv);
    3116                 : 
    3117                 :   // Show alert
    3118                 :   nsCOMPtr<nsIPromptService> prompter =
    3119               0 :     do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv);
    3120               0 :   NS_ENSURE_SUCCESS(rv, rv);
    3121               0 :   return prompter->Alert(dmWindow, title, message);
    3122                 : }

Generated by: LCOV version 1.7