toolkit/components/downloads/nsDownloadManager.cpp

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

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

Integrate suggestion from review to improve consistency with existing code.

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

mercurial