deleted file mode 100644
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ /dev/null
@@ -1,3778 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/DebugOnly.h"
-#include "mozilla/Unused.h"
-
-#include "mozIStorageService.h"
-#include "nsIAlertsService.h"
-#include "nsIArray.h"
-#include "nsIClassInfoImpl.h"
-#include "nsIDOMWindow.h"
-#include "nsIDownloadHistory.h"
-#include "nsIDownloadManagerUI.h"
-#include "nsIFileURL.h"
-#include "nsIMIMEService.h"
-#include "nsIParentalControlsService.h"
-#include "nsIPrefService.h"
-#include "nsIPrivateBrowsingChannel.h"
-#include "nsIPromptService.h"
-#include "nsIPropertyBag2.h"
-#include "nsIResumableChannel.h"
-#include "nsIWebBrowserPersist.h"
-#include "nsIWindowMediator.h"
-#include "nsILocalFileWin.h"
-#include "nsILoadContext.h"
-#include "nsIXULAppInfo.h"
-#include "nsContentUtils.h"
-
-#include "nsAppDirectoryServiceDefs.h"
-#include "nsArrayEnumerator.h"
-#include "nsCExternalHandlerService.h"
-#include "nsCRTGlue.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsDownloadManager.h"
-#include "nsNetUtil.h"
-#include "nsThreadUtils.h"
-#include "prtime.h"
-
-#include "mozStorageCID.h"
-#include "nsDocShellCID.h"
-#include "nsEmbedCID.h"
-#include "nsToolkitCompsCID.h"
-
-#include "mozilla/net/ReferrerPolicy.h"
-
-#include "SQLFunctions.h"
-
-#include "mozilla/Preferences.h"
-
-#ifdef XP_WIN
-#include <shlobj.h>
-#include "nsWindowsHelpers.h"
-#ifdef DOWNLOAD_SCANNER
-#include "nsDownloadScanner.h"
-#endif
-#endif
-
-#ifdef XP_MACOSX
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-#ifdef MOZ_WIDGET_ANDROID
-#include "FennecJNIWrappers.h"
-#endif
-
-#ifdef MOZ_WIDGET_GTK
-#include <gtk/gtk.h>
-#endif
-
-using namespace mozilla;
-using mozilla::downloads::GenerateGUID;
-
-#define DOWNLOAD_MANAGER_BUNDLE "chrome://mozapps/locale/downloads/downloads.properties"
-#define DOWNLOAD_MANAGER_ALERT_ICON "chrome://mozapps/skin/downloads/downloadIcon.png"
-#define PREF_BD_USEJSTRANSFER "browser.download.useJSTransfer"
-#define PREF_BDM_SHOWALERTONCOMPLETE "browser.download.manager.showAlertOnComplete"
-#define PREF_BDM_SHOWALERTINTERVAL "browser.download.manager.showAlertInterval"
-#define PREF_BDM_RETENTION "browser.download.manager.retention"
-#define PREF_BDM_QUITBEHAVIOR "browser.download.manager.quitBehavior"
-#define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
-#define PREF_BDM_SCANWHENDONE "browser.download.manager.scanWhenDone"
-#define PREF_BDM_RESUMEONWAKEDELAY "browser.download.manager.resumeOnWakeDelay"
-#define PREF_BH_DELETETEMPFILEONEXIT "browser.helperApps.deleteTempFileOnExit"
-
-static const int64_t gUpdateInterval = 400 * PR_USEC_PER_MSEC;
-
-#define DM_SCHEMA_VERSION 9
-#define DM_DB_NAME NS_LITERAL_STRING("downloads.sqlite")
-#define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt")
-
-#define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsDownloadManager
-
-NS_IMPL_ISUPPORTS(
- nsDownloadManager
-, nsIDownloadManager
-, nsINavHistoryObserver
-, nsIObserver
-, nsISupportsWeakReference
-)
-
-nsDownloadManager *nsDownloadManager::gDownloadManagerService = nullptr;
-
-nsDownloadManager *
-nsDownloadManager::GetSingleton()
-{
- if (gDownloadManagerService) {
- NS_ADDREF(gDownloadManagerService);
- return gDownloadManagerService;
- }
-
- gDownloadManagerService = new nsDownloadManager();
- if (gDownloadManagerService) {
-#if defined(MOZ_WIDGET_GTK)
- g_type_init();
-#endif
- NS_ADDREF(gDownloadManagerService);
- if (NS_FAILED(gDownloadManagerService->Init()))
- NS_RELEASE(gDownloadManagerService);
- }
-
- return gDownloadManagerService;
-}
-
-nsDownloadManager::~nsDownloadManager()
-{
-#ifdef DOWNLOAD_SCANNER
- if (mScanner) {
- delete mScanner;
- mScanner = nullptr;
- }
-#endif
- gDownloadManagerService = nullptr;
-}
-
-nsresult
-nsDownloadManager::ResumeRetry(nsDownload *aDl)
-{
- // Keep a reference in case we need to cancel the download
- RefPtr<nsDownload> dl = aDl;
-
- // Try to resume the active download
- nsresult rv = dl->Resume();
-
- // If not, try to retry the download
- if (NS_FAILED(rv)) {
- // First cancel the download so it's no longer active
- rv = dl->Cancel();
-
- // Then retry it
- if (NS_SUCCEEDED(rv))
- rv = dl->Retry();
- }
-
- return rv;
-}
-
-nsresult
-nsDownloadManager::PauseAllDownloads(bool aSetResume)
-{
- nsresult rv = PauseAllDownloads(mCurrentDownloads, aSetResume);
- nsresult rv2 = PauseAllDownloads(mCurrentPrivateDownloads, aSetResume);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_SUCCESS(rv2, rv2);
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::PauseAllDownloads(nsCOMArray<nsDownload>& aDownloads, bool aSetResume)
-{
- nsresult retVal = NS_OK;
- for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
- RefPtr<nsDownload> dl = aDownloads[i];
-
- // Only pause things that need to be paused
- if (!dl->IsPaused()) {
- // Set auto-resume before pausing so that it gets into the DB
- dl->mAutoResume = aSetResume ? nsDownload::AUTO_RESUME :
- nsDownload::DONT_RESUME;
-
- // Try to pause the download but don't bail now if we fail
- nsresult rv = dl->Pause();
- if (NS_FAILED(rv))
- retVal = rv;
- }
- }
-
- return retVal;
-}
-
-nsresult
-nsDownloadManager::ResumeAllDownloads(bool aResumeAll)
-{
- nsresult rv = ResumeAllDownloads(mCurrentDownloads, aResumeAll);
- nsresult rv2 = ResumeAllDownloads(mCurrentPrivateDownloads, aResumeAll);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_SUCCESS(rv2, rv2);
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::ResumeAllDownloads(nsCOMArray<nsDownload>& aDownloads, bool aResumeAll)
-{
- nsresult retVal = NS_OK;
- for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
- RefPtr<nsDownload> dl = aDownloads[i];
-
- // If aResumeAll is true, then resume everything; otherwise, check if the
- // download should auto-resume
- if (aResumeAll || dl->ShouldAutoResume()) {
- // Reset auto-resume before retrying so that it gets into the DB through
- // ResumeRetry's eventual call to SetState. We clear the value now so we
- // don't accidentally query completed downloads that were previously
- // auto-resumed (and try to resume them).
- dl->mAutoResume = nsDownload::DONT_RESUME;
-
- // Try to resume/retry the download but don't bail now if we fail
- nsresult rv = ResumeRetry(dl);
- if (NS_FAILED(rv))
- retVal = rv;
- }
- }
-
- return retVal;
-}
-
-nsresult
-nsDownloadManager::RemoveAllDownloads()
-{
- nsresult rv = RemoveAllDownloads(mCurrentDownloads);
- nsresult rv2 = RemoveAllDownloads(mCurrentPrivateDownloads);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_SUCCESS(rv2, rv2);
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::RemoveAllDownloads(nsCOMArray<nsDownload>& aDownloads)
-{
- nsresult rv = NS_OK;
- for (int32_t i = aDownloads.Count() - 1; i >= 0; --i) {
- RefPtr<nsDownload> dl = aDownloads[0];
-
- nsresult result = NS_OK;
- if (!dl->mPrivate && dl->IsPaused() && GetQuitBehavior() != QUIT_AND_CANCEL)
- aDownloads.RemoveObject(dl);
- else
- result = dl->Cancel();
-
- // Track the failure, but don't miss out on other downloads
- if (NS_FAILED(result))
- rv = result;
- }
-
- return rv;
-}
-
-nsresult
-nsDownloadManager::RemoveDownloadsForURI(mozIStorageStatement* aStatement, nsIURI *aURI)
-{
- mozStorageStatementScoper scope(aStatement);
-
- nsAutoCString source;
- nsresult rv = aURI->GetSpec(source);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = aStatement->BindUTF8StringByName(
- NS_LITERAL_CSTRING("source"), source);
- NS_ENSURE_SUCCESS(rv, rv);
-
- bool hasMore = false;
- AutoTArray<nsCString, 4> downloads;
- // Get all the downloads that match the provided URI
- while (NS_SUCCEEDED(aStatement->ExecuteStep(&hasMore)) &&
- hasMore) {
- nsAutoCString downloadGuid;
- rv = aStatement->GetUTF8String(0, downloadGuid);
- NS_ENSURE_SUCCESS(rv, rv);
-
- downloads.AppendElement(downloadGuid);
- }
-
- // Remove each download ignoring any failure so we reach other downloads
- for (int32_t i = downloads.Length(); --i >= 0; )
- (void)RemoveDownload(downloads[i]);
-
- return NS_OK;
-}
-
-void // static
-nsDownloadManager::ResumeOnWakeCallback(nsITimer *aTimer, void *aClosure)
-{
- // Resume the downloads that were set to autoResume
- nsDownloadManager *dlMgr = static_cast<nsDownloadManager *>(aClosure);
- (void)dlMgr->ResumeAllDownloads(false);
-}
-
-already_AddRefed<mozIStorageConnection>
-nsDownloadManager::GetFileDBConnection(nsIFile *dbFile) const
-{
- NS_ASSERTION(dbFile, "GetFileDBConnection called with an invalid nsIFile");
-
- nsCOMPtr<mozIStorageService> storage =
- do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
- NS_ENSURE_TRUE(storage, nullptr);
-
- nsCOMPtr<mozIStorageConnection> conn;
- nsresult rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
- if (rv == NS_ERROR_FILE_CORRUPTED) {
- // delete and try again, since we don't care so much about losing a user's
- // download history
- rv = dbFile->Remove(false);
- NS_ENSURE_SUCCESS(rv, nullptr);
- rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
- }
- NS_ENSURE_SUCCESS(rv, nullptr);
-
- return conn.forget();
-}
-
-already_AddRefed<mozIStorageConnection>
-nsDownloadManager::GetPrivateDBConnection() const
-{
- nsCOMPtr<mozIStorageService> storage =
- do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
- NS_ENSURE_TRUE(storage, nullptr);
-
- nsCOMPtr<mozIStorageConnection> conn;
- nsresult rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(conn));
- NS_ENSURE_SUCCESS(rv, nullptr);
-
- return conn.forget();
-}
-
-void
-nsDownloadManager::CloseAllDBs()
-{
- CloseDB(mDBConn, mUpdateDownloadStatement, mGetIdsForURIStatement);
- CloseDB(mPrivateDBConn, mUpdatePrivateDownloadStatement, mGetPrivateIdsForURIStatement);
-}
-
-void
-nsDownloadManager::CloseDB(mozIStorageConnection* aDBConn,
- mozIStorageStatement* aUpdateStmt,
- mozIStorageStatement* aGetIdsStmt)
-{
- DebugOnly<nsresult> rv = aGetIdsStmt->Finalize();
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- rv = aUpdateStmt->Finalize();
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- rv = aDBConn->AsyncClose(nullptr);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
-}
-
-static nsresult
-InitSQLFunctions(mozIStorageConnection* aDBConn)
-{
- nsresult rv = mozilla::downloads::GenerateGUIDFunction::create(aDBConn);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::InitPrivateDB()
-{
- bool ready = false;
- if (mPrivateDBConn && NS_SUCCEEDED(mPrivateDBConn->GetConnectionReady(&ready)) && ready)
- CloseDB(mPrivateDBConn, mUpdatePrivateDownloadStatement, mGetPrivateIdsForURIStatement);
- mPrivateDBConn = GetPrivateDBConnection();
- if (!mPrivateDBConn)
- return NS_ERROR_NOT_AVAILABLE;
-
- nsresult rv = InitSQLFunctions(mPrivateDBConn);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = CreateTable(mPrivateDBConn);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = InitStatements(mPrivateDBConn, getter_AddRefs(mUpdatePrivateDownloadStatement),
- getter_AddRefs(mGetPrivateIdsForURIStatement));
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::InitFileDB()
-{
- nsresult rv;
-
- nsCOMPtr<nsIFile> dbFile;
- rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
- getter_AddRefs(dbFile));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = dbFile->Append(DM_DB_NAME);
- NS_ENSURE_SUCCESS(rv, rv);
-
- bool ready = false;
- if (mDBConn && NS_SUCCEEDED(mDBConn->GetConnectionReady(&ready)) && ready)
- CloseDB(mDBConn, mUpdateDownloadStatement, mGetIdsForURIStatement);
- mDBConn = GetFileDBConnection(dbFile);
- NS_ENSURE_TRUE(mDBConn, NS_ERROR_NOT_AVAILABLE);
-
- rv = InitSQLFunctions(mDBConn);
- NS_ENSURE_SUCCESS(rv, rv);
-
- bool tableExists;
- rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_downloads"), &tableExists);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (!tableExists) {
- rv = CreateTable(mDBConn);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // We're done with the initialization now and can skip the remaining
- // upgrading logic.
- return NS_OK;
- }
-
- // Checking the database schema now
- int32_t schemaVersion;
- rv = mDBConn->GetSchemaVersion(&schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Changing the database? Be sure to do these two things!
- // 1) Increment DM_SCHEMA_VERSION
- // 2) Implement the proper downgrade/upgrade code for the current version
-
- switch (schemaVersion) {
- // Upgrading
- // Every time you increment the database schema, you need to implement
- // the upgrading code from the previous version to the new one.
- // Also, don't forget to make a unit test to test your upgrading code!
- case 1: // Drop a column (iconURL) from the database (bug 385875)
- {
- // Safely wrap this in a transaction so we don't hose the whole DB
- mozStorageTransaction safeTransaction(mDBConn, true);
-
- // Create a temporary table that will store the existing records
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "CREATE TEMPORARY TABLE moz_downloads_backup ("
- "id INTEGER PRIMARY KEY, "
- "name TEXT, "
- "source TEXT, "
- "target TEXT, "
- "startTime INTEGER, "
- "endTime INTEGER, "
- "state INTEGER"
- ")"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Insert into a temporary table
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "INSERT INTO moz_downloads_backup "
- "SELECT id, name, source, target, startTime, endTime, state "
- "FROM moz_downloads"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Drop the old table
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "DROP TABLE moz_downloads"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Now recreate it with this schema version
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "CREATE TABLE moz_downloads ("
- "id INTEGER PRIMARY KEY, "
- "name TEXT, "
- "source TEXT, "
- "target TEXT, "
- "startTime INTEGER, "
- "endTime INTEGER, "
- "state INTEGER"
- ")"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Insert the data back into it
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "INSERT INTO moz_downloads "
- "SELECT id, name, source, target, startTime, endTime, state "
- "FROM moz_downloads_backup"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // And drop our temporary table
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "DROP TABLE moz_downloads_backup"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Finally, update the schemaVersion variable and the database schema
- schemaVersion = 2;
- rv = mDBConn->SetSchemaVersion(schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to the next upgrade
- MOZ_FALLTHROUGH;
-
- case 2: // Add referrer column to the database
- {
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN referrer TEXT"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Finally, update the schemaVersion variable and the database schema
- schemaVersion = 3;
- rv = mDBConn->SetSchemaVersion(schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to the next upgrade
- MOZ_FALLTHROUGH;
-
- case 3: // This version adds a column to the database (entityID)
- {
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN entityID TEXT"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Finally, update the schemaVersion variable and the database schema
- schemaVersion = 4;
- rv = mDBConn->SetSchemaVersion(schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to the next upgrade
- MOZ_FALLTHROUGH;
-
- case 4: // This version adds a column to the database (tempPath)
- {
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN tempPath TEXT"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Finally, update the schemaVersion variable and the database schema
- schemaVersion = 5;
- rv = mDBConn->SetSchemaVersion(schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to the next upgrade
- MOZ_FALLTHROUGH;
-
- case 5: // This version adds two columns for tracking transfer progress
- {
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN currBytes INTEGER NOT NULL DEFAULT 0"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN maxBytes INTEGER NOT NULL DEFAULT -1"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Finally, update the schemaVersion variable and the database schema
- schemaVersion = 6;
- rv = mDBConn->SetSchemaVersion(schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to the next upgrade
- MOZ_FALLTHROUGH;
-
- case 6: // This version adds three columns to DB (MIME type related info)
- {
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN mimeType TEXT"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN preferredApplication TEXT"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN preferredAction INTEGER NOT NULL DEFAULT 0"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Finally, update the schemaVersion variable and the database schema
- schemaVersion = 7;
- rv = mDBConn->SetSchemaVersion(schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to next upgrade
- MOZ_FALLTHROUGH;
-
- case 7: // This version adds a column to remember to auto-resume downloads
- {
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads "
- "ADD COLUMN autoResume INTEGER NOT NULL DEFAULT 0"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Finally, update the schemaVersion variable and the database schema
- schemaVersion = 8;
- rv = mDBConn->SetSchemaVersion(schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to the next upgrade
- MOZ_FALLTHROUGH;
-
- // Warning: schema versions >=8 must take into account that they can
- // be operating on schemas from unknown, future versions that have
- // been downgraded. Operations such as adding columns may fail,
- // since the column may already exist.
-
- case 8: // This version adds a column for GUIDs
- {
- bool exists;
- rv = mDBConn->IndexExists(NS_LITERAL_CSTRING("moz_downloads_guid_uniqueindex"),
- &exists);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!exists) {
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "ALTER TABLE moz_downloads ADD COLUMN guid TEXT"));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "CREATE UNIQUE INDEX moz_downloads_guid_uniqueindex ON moz_downloads (guid)"));
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "UPDATE moz_downloads SET guid = GENERATE_GUID() WHERE guid ISNULL"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Finally, update the database schema
- schemaVersion = 9;
- rv = mDBConn->SetSchemaVersion(schemaVersion);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to the next upgrade
-
- // Extra sanity checking for developers
-#ifndef DEBUG
- MOZ_FALLTHROUGH;
- case DM_SCHEMA_VERSION:
-#endif
- break;
-
- case 0:
- {
- NS_WARNING("Could not get download database's schema version!");
-
- // The table may still be usable - someone may have just messed with the
- // schema version, so let's just treat this like a downgrade and verify
- // that the needed columns are there. If they aren't there, we'll drop
- // the table anyway.
- rv = mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Fallthrough to downgrade check
- MOZ_FALLTHROUGH;
-
- // Downgrading
- // If columns have been added to the table, we can still use the ones we
- // understand safely. If columns have been deleted or alterd, we just
- // drop the table and start from scratch. If you change how a column
- // should be interpreted, make sure you also change its name so this
- // check will catch it.
- default:
- {
- nsCOMPtr<mozIStorageStatement> stmt;
- rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "SELECT id, name, source, target, tempPath, startTime, endTime, state, "
- "referrer, entityID, currBytes, maxBytes, mimeType, "
- "preferredApplication, preferredAction, autoResume, guid "
- "FROM moz_downloads"), getter_AddRefs(stmt));
- if (NS_SUCCEEDED(rv)) {
- // We have a database that contains all of the elements that make up
- // the latest known schema. Reset the version to force an upgrade
- // path if this downgraded database is used in a later version.
- mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
- break;
- }
-
- // if the statement fails, that means all the columns were not there.
- // First we backup the database
- nsCOMPtr<mozIStorageService> storage =
- do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
- NS_ENSURE_TRUE(storage, NS_ERROR_NOT_AVAILABLE);
- nsCOMPtr<nsIFile> backup;
- rv = storage->BackupDatabaseFile(dbFile, DM_DB_CORRUPT_FILENAME, nullptr,
- getter_AddRefs(backup));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Then we dump it
- rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "DROP TABLE moz_downloads"));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = CreateTable(mDBConn);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- break;
- }
-
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::CreateTable(mozIStorageConnection* aDBConn)
-{
- nsresult rv = aDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
- if (NS_FAILED(rv)) return rv;
-
- rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "CREATE TABLE moz_downloads ("
- "id INTEGER PRIMARY KEY, "
- "name TEXT, "
- "source TEXT, "
- "target TEXT, "
- "tempPath TEXT, "
- "startTime INTEGER, "
- "endTime INTEGER, "
- "state INTEGER, "
- "referrer TEXT, "
- "entityID TEXT, "
- "currBytes INTEGER NOT NULL DEFAULT 0, "
- "maxBytes INTEGER NOT NULL DEFAULT -1, "
- "mimeType TEXT, "
- "preferredApplication TEXT, "
- "preferredAction INTEGER NOT NULL DEFAULT 0, "
- "autoResume INTEGER NOT NULL DEFAULT 0, "
- "guid TEXT"
- ")"));
- if (NS_FAILED(rv)) return rv;
-
- rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
- "CREATE UNIQUE INDEX moz_downloads_guid_uniqueindex "
- "ON moz_downloads(guid)"));
- return rv;
-}
-
-nsresult
-nsDownloadManager::RestoreDatabaseState()
-{
- // Restore downloads that were in a scanning state. We can assume that they
- // have been dealt with by the virus scanner
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "UPDATE moz_downloads "
- "SET state = :state "
- "WHERE state = :state_cond"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state_cond"), nsIDownloadManager::DOWNLOAD_SCANNING);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Convert supposedly-active downloads into downloads that should auto-resume
- rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "UPDATE moz_downloads "
- "SET autoResume = :autoResume "
- "WHERE state = :notStarted "
- "OR state = :queued "
- "OR state = :downloading"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::AUTO_RESUME);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("notStarted"), nsIDownloadManager::DOWNLOAD_NOTSTARTED);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Switch any download that is supposed to automatically resume and is in a
- // finished state to *not* automatically resume. See Bug 409179 for details.
- rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "UPDATE moz_downloads "
- "SET autoResume = :autoResume "
- "WHERE state = :state "
- "AND autoResume = :autoResume_cond"),
- getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume_cond"), nsDownload::AUTO_RESUME);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
-
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::RestoreActiveDownloads()
-{
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "SELECT id "
- "FROM moz_downloads "
- "WHERE (state = :state AND LENGTH(entityID) > 0) "
- "OR autoResume != :autoResume"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_PAUSED);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsresult retVal = NS_OK;
- bool hasResults;
- while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResults)) && hasResults) {
- RefPtr<nsDownload> dl;
- // Keep trying to add even if we fail one, but make sure to return failure.
- // Additionally, be careful to not call anything that tries to change the
- // database because we're iterating over a live statement.
- if (NS_FAILED(GetDownloadFromDB(stmt->AsInt32(0), getter_AddRefs(dl))) ||
- NS_FAILED(AddToCurrentDownloads(dl)))
- retVal = NS_ERROR_FAILURE;
- }
-
- // Try to resume only the downloads that should auto-resume
- rv = ResumeAllDownloads(false);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return retVal;
-}
-
-int64_t
-nsDownloadManager::AddDownloadToDB(const nsAString &aName,
- const nsACString &aSource,
- const nsACString &aTarget,
- const nsAString &aTempPath,
- int64_t aStartTime,
- int64_t aEndTime,
- const nsACString &aMimeType,
- const nsACString &aPreferredApp,
- nsHandlerInfoAction aPreferredAction,
- bool aPrivate,
- nsACString& aNewGUID)
-{
- mozIStorageConnection* dbConn = aPrivate ? mPrivateDBConn : mDBConn;
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
- "INSERT INTO moz_downloads "
- "(name, source, target, tempPath, startTime, endTime, state, "
- "mimeType, preferredApplication, preferredAction, guid) VALUES "
- "(:name, :source, :target, :tempPath, :startTime, :endTime, :state, "
- ":mimeType, :preferredApplication, :preferredAction, :guid)"),
- getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("source"), aSource);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("target"), aTarget);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), aTempPath);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_NOTSTARTED);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mimeType"), aMimeType);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("preferredApplication"), aPreferredApp);
- NS_ENSURE_SUCCESS(rv, 0);
-
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("preferredAction"), aPreferredAction);
- NS_ENSURE_SUCCESS(rv, 0);
-
- nsAutoCString guid;
- rv = GenerateGUID(guid);
- NS_ENSURE_SUCCESS(rv, 0);
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
- NS_ENSURE_SUCCESS(rv, 0);
-
- bool hasMore;
- rv = stmt->ExecuteStep(&hasMore); // we want to keep our lock
- NS_ENSURE_SUCCESS(rv, 0);
-
- int64_t id = 0;
- rv = dbConn->GetLastInsertRowID(&id);
- NS_ENSURE_SUCCESS(rv, 0);
-
- aNewGUID = guid;
-
- // lock on DB from statement will be released once we return
- return id;
-}
-
-nsresult
-nsDownloadManager::InitDB()
-{
- nsresult rv = InitPrivateDB();
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = InitFileDB();
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = InitStatements(mDBConn, getter_AddRefs(mUpdateDownloadStatement),
- getter_AddRefs(mGetIdsForURIStatement));
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::InitStatements(mozIStorageConnection* aDBConn,
- mozIStorageStatement** aUpdateStatement,
- mozIStorageStatement** aGetIdsStatement)
-{
- nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "UPDATE moz_downloads "
- "SET tempPath = :tempPath, startTime = :startTime, endTime = :endTime, "
- "state = :state, referrer = :referrer, entityID = :entityID, "
- "currBytes = :currBytes, maxBytes = :maxBytes, autoResume = :autoResume "
- "WHERE id = :id"), aUpdateStatement);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "SELECT guid "
- "FROM moz_downloads "
- "WHERE source = :source"), aGetIdsStatement);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::Init()
-{
- nsresult rv;
-
- nsCOMPtr<nsIStringBundleService> bundleService =
- mozilla::services::GetStringBundleService();
- if (!bundleService)
- return NS_ERROR_FAILURE;
-
- rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE,
- getter_AddRefs(mBundle));
- NS_ENSURE_SUCCESS(rv, rv);
-
- mUseJSTransfer = true;
-
- if (mUseJSTransfer)
- return NS_OK;
-
- // Clean up any old downloads.rdf files from before Firefox 3
- {
- nsCOMPtr<nsIFile> oldDownloadsFile;
- bool fileExists;
- if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE,
- getter_AddRefs(oldDownloadsFile))) &&
- NS_SUCCEEDED(oldDownloadsFile->Exists(&fileExists)) &&
- fileExists) {
- (void)oldDownloadsFile->Remove(false);
- }
- }
-
- mObserverService = mozilla::services::GetObserverService();
- if (!mObserverService)
- return NS_ERROR_FAILURE;
-
- rv = InitDB();
- NS_ENSURE_SUCCESS(rv, rv);
-
-#ifdef DOWNLOAD_SCANNER
- mScanner = new nsDownloadScanner();
- if (!mScanner)
- return NS_ERROR_OUT_OF_MEMORY;
- rv = mScanner->Init();
- if (NS_FAILED(rv)) {
- delete mScanner;
- mScanner = nullptr;
- }
-#endif
-
- // Do things *after* initializing various download manager properties such as
- // restoring downloads to a consistent state
- rv = RestoreDatabaseState();
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = RestoreActiveDownloads();
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
- "Failed to restore all active downloads");
-
- nsCOMPtr<nsINavHistoryService> history =
- do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
-
- (void)mObserverService->NotifyObservers(
- static_cast<nsIDownloadManager *>(this),
- "download-manager-initialized",
- nullptr);
-
- // The following AddObserver calls must be the last lines in this function,
- // because otherwise, this function may fail (and thus, this object would be not
- // completely initialized), but the observerservice would still keep a reference
- // to us and notify us about shutdown, which may cause crashes.
- // failure to add an observer is not critical
- (void)mObserverService->AddObserver(this, "quit-application", true);
- (void)mObserverService->AddObserver(this, "quit-application-requested", true);
- (void)mObserverService->AddObserver(this, "offline-requested", true);
- (void)mObserverService->AddObserver(this, "sleep_notification", true);
- (void)mObserverService->AddObserver(this, "wake_notification", true);
- (void)mObserverService->AddObserver(this, "suspend_process_notification", true);
- (void)mObserverService->AddObserver(this, "resume_process_notification", true);
- (void)mObserverService->AddObserver(this, "profile-before-change", true);
- (void)mObserverService->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, true);
- (void)mObserverService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, true);
- (void)mObserverService->AddObserver(this, "last-pb-context-exited", true);
- (void)mObserverService->AddObserver(this, "last-pb-context-exiting", true);
-
- if (history)
- (void)history->AddObserver(this, true);
-
- return NS_OK;
-}
-
-int32_t
-nsDownloadManager::GetRetentionBehavior()
-{
- // We use 0 as the default, which is "remove when done"
- nsresult rv;
- nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, 0);
-
- int32_t val;
- rv = pref->GetIntPref(PREF_BDM_RETENTION, &val);
- NS_ENSURE_SUCCESS(rv, 0);
-
- // Allow the Downloads Panel to change the retention behavior. We do this to
- // allow proper migration to the new feature when using the same profile on
- // multiple versions of the product (bug 697678). Implementation note: in
- // order to allow observers to change the retention value, we have to pass an
- // object in the aSubject parameter, we cannot use aData for that.
- nsCOMPtr<nsISupportsPRInt32> retentionBehavior =
- do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID);
- retentionBehavior->SetData(val);
- (void)mObserverService->NotifyObservers(retentionBehavior,
- "download-manager-change-retention",
- nullptr);
- retentionBehavior->GetData(&val);
-
- return val;
-}
-
-enum nsDownloadManager::QuitBehavior
-nsDownloadManager::GetQuitBehavior()
-{
- // We use 0 as the default, which is "remember and resume the download"
- nsresult rv;
- nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME);
-
- int32_t val;
- rv = pref->GetIntPref(PREF_BDM_QUITBEHAVIOR, &val);
- NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME);
-
- switch (val) {
- case 1:
- return QUIT_AND_PAUSE;
- case 2:
- return QUIT_AND_CANCEL;
- default:
- return QUIT_AND_RESUME;
- }
-}
-
-// Using a globally-unique GUID, search all databases (both private and public).
-// A return value of NS_ERROR_NOT_AVAILABLE means no download with the given GUID
-// could be found, either private or public.
-
-nsresult
-nsDownloadManager::GetDownloadFromDB(const nsACString& aGUID, nsDownload **retVal)
-{
- MOZ_ASSERT(!FindDownload(aGUID),
- "If it is a current download, you should not call this method!");
-
- NS_NAMED_LITERAL_CSTRING(query,
- "SELECT id, state, startTime, source, target, tempPath, name, referrer, "
- "entityID, currBytes, maxBytes, mimeType, preferredAction, "
- "preferredApplication, autoResume, guid "
- "FROM moz_downloads "
- "WHERE guid = :guid");
- // First, let's query the database and see if it even exists
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = mDBConn->CreateStatement(query, getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = GetDownloadFromDB(mDBConn, stmt, retVal);
-
- // If the download cannot be found in the public database, try again
- // in the private one. Otherwise, return whatever successful result
- // or failure obtained from the public database.
- if (rv == NS_ERROR_NOT_AVAILABLE) {
- rv = mPrivateDBConn->CreateStatement(query, getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = GetDownloadFromDB(mPrivateDBConn, stmt, retVal);
-
- // Only if it still cannot be found do we report the failure.
- if (rv == NS_ERROR_NOT_AVAILABLE) {
- *retVal = nullptr;
- }
- }
- return rv;
-}
-
-nsresult
-nsDownloadManager::GetDownloadFromDB(uint32_t aID, nsDownload **retVal)
-{
- NS_WARNING("Using integer IDs without compat mode enabled");
-
- MOZ_ASSERT(!FindDownload(aID),
- "If it is a current download, you should not call this method!");
-
- // First, let's query the database and see if it even exists
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "SELECT id, state, startTime, source, target, tempPath, name, referrer, "
- "entityID, currBytes, maxBytes, mimeType, preferredAction, "
- "preferredApplication, autoResume, guid "
- "FROM moz_downloads "
- "WHERE id = :id"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return GetDownloadFromDB(mDBConn, stmt, retVal);
-}
-
-nsresult
-nsDownloadManager::GetDownloadFromDB(mozIStorageConnection* aDBConn,
- mozIStorageStatement* stmt,
- nsDownload **retVal)
-{
- bool hasResults = false;
- nsresult rv = stmt->ExecuteStep(&hasResults);
- if (NS_FAILED(rv) || !hasResults)
- return NS_ERROR_NOT_AVAILABLE;
-
- // We have a download, so lets create it
- RefPtr<nsDownload> dl = new nsDownload();
- if (!dl)
- return NS_ERROR_OUT_OF_MEMORY;
- dl->mPrivate = aDBConn == mPrivateDBConn;
-
- dl->mDownloadManager = this;
-
- int32_t i = 0;
- // Setting all properties of the download now
- dl->mCancelable = nullptr;
- dl->mID = stmt->AsInt64(i++);
- dl->mDownloadState = stmt->AsInt32(i++);
- dl->mStartTime = stmt->AsInt64(i++);
-
- nsCString source;
- stmt->GetUTF8String(i++, source);
- rv = NS_NewURI(getter_AddRefs(dl->mSource), source);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCString target;
- stmt->GetUTF8String(i++, target);
- rv = NS_NewURI(getter_AddRefs(dl->mTarget), target);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsString tempPath;
- stmt->GetString(i++, tempPath);
- if (!tempPath.IsEmpty()) {
- rv = NS_NewLocalFile(tempPath, true, getter_AddRefs(dl->mTempFile));
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- stmt->GetString(i++, dl->mDisplayName);
-
- nsCString referrer;
- rv = stmt->GetUTF8String(i++, referrer);
- if (NS_SUCCEEDED(rv) && !referrer.IsEmpty()) {
- rv = NS_NewURI(getter_AddRefs(dl->mReferrer), referrer);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- rv = stmt->GetUTF8String(i++, dl->mEntityID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- int64_t currBytes = stmt->AsInt64(i++);
- int64_t maxBytes = stmt->AsInt64(i++);
- dl->SetProgressBytes(currBytes, maxBytes);
-
- // Build mMIMEInfo only if the mimeType in DB is not empty
- nsAutoCString mimeType;
- rv = stmt->GetUTF8String(i++, mimeType);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (!mimeType.IsEmpty()) {
- nsCOMPtr<nsIMIMEService> mimeService =
- do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
- getter_AddRefs(dl->mMIMEInfo));
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsHandlerInfoAction action = stmt->AsInt32(i++);
- rv = dl->mMIMEInfo->SetPreferredAction(action);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsAutoCString persistentDescriptor;
- rv = stmt->GetUTF8String(i++, persistentDescriptor);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (!persistentDescriptor.IsEmpty()) {
- nsCOMPtr<nsILocalHandlerApp> handler =
- do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIFile> localExecutable;
- rv = NS_NewNativeLocalFile(EmptyCString(), false,
- getter_AddRefs(localExecutable));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = localExecutable->SetPersistentDescriptor(persistentDescriptor);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = handler->SetExecutable(localExecutable);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = dl->mMIMEInfo->SetPreferredApplicationHandler(handler);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- } else {
- // Compensate for the i++s skipped in the true block
- i += 2;
- }
-
- dl->mAutoResume =
- static_cast<enum nsDownload::AutoResume>(stmt->AsInt32(i++));
-
- rv = stmt->GetUTF8String(i++, dl->mGUID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Handle situations where we load a download from a database that has been
- // used in an older version and not gone through the upgrade path (ie. it
- // contains empty GUID entries).
- if (dl->mGUID.IsEmpty()) {
- rv = GenerateGUID(dl->mGUID);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<mozIStorageStatement> updateStmt;
- rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "UPDATE moz_downloads SET guid = :guid "
- "WHERE id = :id"),
- getter_AddRefs(updateStmt));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = updateStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), dl->mGUID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), dl->mID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = updateStmt->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- // Addrefing and returning
- dl.forget(retVal);
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::AddToCurrentDownloads(nsDownload *aDl)
-{
- nsCOMArray<nsDownload>& currentDownloads =
- aDl->mPrivate ? mCurrentPrivateDownloads : mCurrentDownloads;
- if (!currentDownloads.AppendObject(aDl))
- return NS_ERROR_OUT_OF_MEMORY;
-
- aDl->mDownloadManager = this;
- return NS_OK;
-}
-
-void
-nsDownloadManager::SendEvent(nsDownload *aDownload, const char *aTopic)
-{
- (void)mObserverService->NotifyObservers(aDownload, aTopic, nullptr);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIDownloadManager
-
-NS_IMETHODIMP
-nsDownloadManager::GetActivePrivateDownloadCount(int32_t* aResult)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- *aResult = mCurrentPrivateDownloads.Count();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::GetActiveDownloadCount(int32_t *aResult)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- *aResult = mCurrentDownloads.Count();
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::GetActiveDownloads(nsISimpleEnumerator **aResult)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- return NS_NewArrayEnumerator(aResult, mCurrentDownloads);
-}
-
-NS_IMETHODIMP
-nsDownloadManager::GetActivePrivateDownloads(nsISimpleEnumerator **aResult)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- return NS_NewArrayEnumerator(aResult, mCurrentPrivateDownloads);
-}
-
-/**
- * For platforms where helper apps use the downloads directory (i.e. mobile),
- * this should be kept in sync with nsExternalHelperAppService.cpp
- */
-NS_IMETHODIMP
-nsDownloadManager::GetDefaultDownloadsDirectory(nsIFile **aResult)
-{
- nsCOMPtr<nsIFile> downloadDir;
-
- nsresult rv;
- nsCOMPtr<nsIProperties> dirService =
- do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // OSX 10.4:
- // Desktop
- // OSX 10.5:
- // User download directory
- // Vista:
- // Downloads
- // XP/2K:
- // My Documents/Downloads
- // Linux:
- // XDG user dir spec, with a fallback to Home/Downloads
-
- nsXPIDLString folderName;
- mBundle->GetStringFromName(u"downloadsFolder",
- getter_Copies(folderName));
-
-#if defined (XP_MACOSX)
- rv = dirService->Get(NS_OSX_DEFAULT_DOWNLOAD_DIR,
- NS_GET_IID(nsIFile),
- getter_AddRefs(downloadDir));
- NS_ENSURE_SUCCESS(rv, rv);
-#elif defined(XP_WIN)
- rv = dirService->Get(NS_WIN_DEFAULT_DOWNLOAD_DIR,
- NS_GET_IID(nsIFile),
- getter_AddRefs(downloadDir));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Check the os version
- nsCOMPtr<nsIPropertyBag2> infoService =
- do_GetService(NS_SYSTEMINFO_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- int32_t version;
- NS_NAMED_LITERAL_STRING(osVersion, "version");
- rv = infoService->GetPropertyAsInt32(osVersion, &version);
- NS_ENSURE_SUCCESS(rv, rv);
- if (version < 6) { // XP/2K
- // First get "My Documents"
- rv = dirService->Get(NS_WIN_PERSONAL_DIR,
- NS_GET_IID(nsIFile),
- getter_AddRefs(downloadDir));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = downloadDir->Append(folderName);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // This could be the first time we are creating the downloads folder in My
- // Documents, so make sure it exists.
- bool exists;
- rv = downloadDir->Exists(&exists);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!exists) {
- rv = downloadDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
-#elif defined(XP_UNIX)
-#if defined(MOZ_WIDGET_ANDROID)
- // Android doesn't have a $HOME directory, and by default we only have
- // write access to /data/data/org.mozilla.{$APP} and /sdcard
- char* downloadDirPath = getenv("DOWNLOADS_DIRECTORY");
- if (downloadDirPath) {
- rv = NS_NewNativeLocalFile(nsDependentCString(downloadDirPath),
- true, getter_AddRefs(downloadDir));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- else {
- rv = NS_ERROR_FAILURE;
- }
-#else
- rv = dirService->Get(NS_UNIX_DEFAULT_DOWNLOAD_DIR,
- NS_GET_IID(nsIFile),
- getter_AddRefs(downloadDir));
- // fallback to Home/Downloads
- if (NS_FAILED(rv)) {
- rv = dirService->Get(NS_UNIX_HOME_DIR,
- NS_GET_IID(nsIFile),
- getter_AddRefs(downloadDir));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = downloadDir->Append(folderName);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-#endif
-#else
- rv = dirService->Get(NS_OS_HOME_DIR,
- NS_GET_IID(nsIFile),
- getter_AddRefs(downloadDir));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = downloadDir->Append(folderName);
- NS_ENSURE_SUCCESS(rv, rv);
-#endif
-
- downloadDir.forget(aResult);
-
- return NS_OK;
-}
-
-#define NS_BRANCH_DOWNLOAD "browser.download."
-#define NS_PREF_FOLDERLIST "folderList"
-#define NS_PREF_DIR "dir"
-
-NS_IMETHODIMP
-nsDownloadManager::GetUserDownloadsDirectory(nsIFile **aResult)
-{
- nsresult rv;
- nsCOMPtr<nsIProperties> dirService =
- do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIPrefService> prefService =
- do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIPrefBranch> prefBranch;
- rv = prefService->GetBranch(NS_BRANCH_DOWNLOAD,
- getter_AddRefs(prefBranch));
- NS_ENSURE_SUCCESS(rv, rv);
-
- int32_t val;
- rv = prefBranch->GetIntPref(NS_PREF_FOLDERLIST,
- &val);
- NS_ENSURE_SUCCESS(rv, rv);
-
- switch(val) {
- case 0: // Desktop
- {
- nsCOMPtr<nsIFile> downloadDir;
- rv = dirService->Get(NS_OS_DESKTOP_DIR,
- NS_GET_IID(nsIFile),
- getter_AddRefs(downloadDir));
- NS_ENSURE_SUCCESS(rv, rv);
- downloadDir.forget(aResult);
- return NS_OK;
- }
- break;
- case 1: // Downloads
- return GetDefaultDownloadsDirectory(aResult);
- case 2: // Custom
- {
- nsCOMPtr<nsIFile> customDirectory;
- prefBranch->GetComplexValue(NS_PREF_DIR,
- NS_GET_IID(nsIFile),
- getter_AddRefs(customDirectory));
- if (customDirectory) {
- bool exists = false;
- (void)customDirectory->Exists(&exists);
-
- if (!exists) {
- rv = customDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
- if (NS_SUCCEEDED(rv)) {
- customDirectory.forget(aResult);
- return NS_OK;
- }
-
- // Create failed, so it still doesn't exist. Fall out and get the
- // default downloads directory.
- }
-
- bool writable = false;
- bool directory = false;
- (void)customDirectory->IsWritable(&writable);
- (void)customDirectory->IsDirectory(&directory);
-
- if (exists && writable && directory) {
- customDirectory.forget(aResult);
- return NS_OK;
- }
- }
- rv = GetDefaultDownloadsDirectory(aResult);
- if (NS_SUCCEEDED(rv)) {
- (void)prefBranch->SetComplexValue(NS_PREF_DIR,
- NS_GET_IID(nsIFile),
- *aResult);
- }
- return rv;
- }
- break;
- }
- return NS_ERROR_INVALID_ARG;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::AddDownload(DownloadType aDownloadType,
- nsIURI *aSource,
- nsIURI *aTarget,
- const nsAString& aDisplayName,
- nsIMIMEInfo *aMIMEInfo,
- PRTime aStartTime,
- nsIFile *aTempFile,
- nsICancelable *aCancelable,
- bool aIsPrivate,
- nsIDownload **aDownload)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_ENSURE_ARG_POINTER(aSource);
- NS_ENSURE_ARG_POINTER(aTarget);
- NS_ENSURE_ARG_POINTER(aDownload);
-
- nsresult rv;
-
- // target must be on the local filesystem
- nsCOMPtr<nsIFileURL> targetFileURL = do_QueryInterface(aTarget, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIFile> targetFile;
- rv = targetFileURL->GetFile(getter_AddRefs(targetFile));
- NS_ENSURE_SUCCESS(rv, rv);
-
- RefPtr<nsDownload> dl = new nsDownload();
- if (!dl)
- return NS_ERROR_OUT_OF_MEMORY;
-
- // give our new nsIDownload some info so it's ready to go off into the world
- dl->mTarget = aTarget;
- dl->mSource = aSource;
- dl->mTempFile = aTempFile;
- dl->mPrivate = aIsPrivate;
-
- dl->mDisplayName = aDisplayName;
- if (dl->mDisplayName.IsEmpty())
- targetFile->GetLeafName(dl->mDisplayName);
-
- dl->mMIMEInfo = aMIMEInfo;
- dl->SetStartTime(aStartTime == 0 ? PR_Now() : aStartTime);
-
- // Creates a cycle that will be broken when the download finishes
- dl->mCancelable = aCancelable;
-
- // Adding to the DB
- nsAutoCString source, target;
- rv = aSource->GetSpec(source);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = aTarget->GetSpec(target);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Track the temp file for exthandler downloads
- nsAutoString tempPath;
- if (aTempFile)
- aTempFile->GetPath(tempPath);
-
- // Break down MIMEInfo but don't panic if we can't get all the pieces - we
- // can still download the file
- nsAutoCString persistentDescriptor, mimeType;
- nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
- if (aMIMEInfo) {
- (void)aMIMEInfo->GetType(mimeType);
-
- nsCOMPtr<nsIHandlerApp> handlerApp;
- (void)aMIMEInfo->GetPreferredApplicationHandler(getter_AddRefs(handlerApp));
- nsCOMPtr<nsILocalHandlerApp> locHandlerApp = do_QueryInterface(handlerApp);
-
- if (locHandlerApp) {
- nsCOMPtr<nsIFile> executable;
- (void)locHandlerApp->GetExecutable(getter_AddRefs(executable));
- Unused << executable->GetPersistentDescriptor(persistentDescriptor);
- }
-
- (void)aMIMEInfo->GetPreferredAction(&action);
- }
-
- int64_t id = AddDownloadToDB(dl->mDisplayName, source, target, tempPath,
- dl->mStartTime, dl->mLastUpdate,
- mimeType, persistentDescriptor, action,
- dl->mPrivate, dl->mGUID /* outparam */);
- NS_ENSURE_TRUE(id, NS_ERROR_FAILURE);
- dl->mID = id;
-
- rv = AddToCurrentDownloads(dl);
- (void)dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED);
- NS_ENSURE_SUCCESS(rv, rv);
-
-#ifdef DOWNLOAD_SCANNER
- if (mScanner) {
- bool scan = true;
- nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
- if (prefs) {
- (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan);
- }
- // We currently apply local security policy to downloads when we scan
- // via windows all-in-one download security api. The CheckPolicy call
- // below is a pre-emptive part of that process. So tie applying security
- // zone policy settings when downloads are intiated to the same pref
- // that triggers applying security zone policy settings after a download
- // completes. (bug 504804)
- if (scan) {
- AVCheckPolicyState res = mScanner->CheckPolicy(aSource, aTarget);
- if (res == AVPOLICY_BLOCKED) {
- // This download will get deleted during a call to IAE's Save,
- // so go ahead and mark it as blocked and avoid the download.
- (void)CancelDownload(id);
- (void)dl->SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY);
- }
- }
- }
-#endif
-
- // Check with parental controls to see if file downloads
- // are allowed for this user. If not allowed, cancel the
- // download and mark its state as being blocked.
- nsCOMPtr<nsIParentalControlsService> pc =
- do_CreateInstance(NS_PARENTALCONTROLSSERVICE_CONTRACTID);
- if (pc) {
- bool enabled = false;
- (void)pc->GetBlockFileDownloadsEnabled(&enabled);
- if (enabled) {
- (void)CancelDownload(id);
- (void)dl->SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL);
- }
-
- // Log the event if required by pc settings.
- bool logEnabled = false;
- (void)pc->GetLoggingEnabled(&logEnabled);
- if (logEnabled) {
- (void)pc->Log(nsIParentalControlsService::ePCLog_FileDownload,
- enabled,
- aSource,
- nullptr);
- }
- }
-
- dl.forget(aDownload);
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::GetDownload(uint32_t aID, nsIDownload **aDownloadItem)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_WARNING("Using integer IDs without compat mode enabled");
-
- nsDownload *itm = FindDownload(aID);
-
- RefPtr<nsDownload> dl;
- if (!itm) {
- nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
- NS_ENSURE_SUCCESS(rv, rv);
-
- itm = dl.get();
- }
-
- NS_ADDREF(*aDownloadItem = itm);
-
- return NS_OK;
-}
-
-namespace {
-class AsyncResult : public Runnable
-{
-public:
- AsyncResult(nsresult aStatus, nsIDownload* aResult,
- nsIDownloadManagerResult* aCallback)
- : mStatus(aStatus), mResult(aResult), mCallback(aCallback)
- {
- }
-
- NS_IMETHOD Run() override
- {
- mCallback->HandleResult(mStatus, mResult);
- return NS_OK;
- }
-
-private:
- nsresult mStatus;
- nsCOMPtr<nsIDownload> mResult;
- nsCOMPtr<nsIDownloadManagerResult> mCallback;
-};
-} // namespace
-
-NS_IMETHODIMP
-nsDownloadManager::GetDownloadByGUID(const nsACString& aGUID,
- nsIDownloadManagerResult* aCallback)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- nsDownload *itm = FindDownload(aGUID);
-
- nsresult rv = NS_OK;
- RefPtr<nsDownload> dl;
- if (!itm) {
- rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl));
- itm = dl.get();
- }
-
- RefPtr<AsyncResult> runnable = new AsyncResult(rv, itm, aCallback);
- NS_DispatchToMainThread(runnable);
- return NS_OK;
-}
-
-nsDownload *
-nsDownloadManager::FindDownload(uint32_t aID)
-{
- // we shouldn't ever have many downloads, so we can loop over them
- for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
- nsDownload *dl = mCurrentDownloads[i];
- if (dl->mID == aID)
- return dl;
- }
-
- return nullptr;
-}
-
-nsDownload *
-nsDownloadManager::FindDownload(const nsACString& aGUID)
-{
- // we shouldn't ever have many downloads, so we can loop over them
- for (int32_t i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
- nsDownload *dl = mCurrentDownloads[i];
- if (dl->mGUID == aGUID)
- return dl;
- }
-
- for (int32_t i = mCurrentPrivateDownloads.Count() - 1; i >= 0; --i) {
- nsDownload *dl = mCurrentPrivateDownloads[i];
- if (dl->mGUID == aGUID)
- return dl;
- }
-
- return nullptr;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::CancelDownload(uint32_t aID)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_WARNING("Using integer IDs without compat mode enabled");
-
- // We AddRef here so we don't lose access to member variables when we remove
- RefPtr<nsDownload> dl = FindDownload(aID);
-
- // if it's null, someone passed us a bad id.
- if (!dl)
- return NS_ERROR_FAILURE;
-
- return dl->Cancel();
-}
-
-nsresult
-nsDownloadManager::RetryDownload(const nsACString& aGUID)
-{
- RefPtr<nsDownload> dl;
- nsresult rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl));
- NS_ENSURE_SUCCESS(rv, rv);
-
- return RetryDownload(dl);
-}
-
-NS_IMETHODIMP
-nsDownloadManager::RetryDownload(uint32_t aID)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_WARNING("Using integer IDs without compat mode enabled");
-
- RefPtr<nsDownload> dl;
- nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
- NS_ENSURE_SUCCESS(rv, rv);
-
- return RetryDownload(dl);
-}
-
-nsresult
-nsDownloadManager::RetryDownload(nsDownload* dl)
-{
- // if our download is not canceled or failed, we should fail
- if (dl->mDownloadState != nsIDownloadManager::DOWNLOAD_FAILED &&
- dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL &&
- dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY &&
- dl->mDownloadState != nsIDownloadManager::DOWNLOAD_DIRTY &&
- dl->mDownloadState != nsIDownloadManager::DOWNLOAD_CANCELED)
- return NS_ERROR_FAILURE;
-
- // If the download has failed and is resumable then we first try resuming it
- nsresult rv;
- if (dl->mDownloadState == nsIDownloadManager::DOWNLOAD_FAILED && dl->IsResumable()) {
- rv = dl->Resume();
- if (NS_SUCCEEDED(rv))
- return rv;
- }
-
- // reset time and download progress
- dl->SetStartTime(PR_Now());
- dl->SetProgressBytes(0, -1);
-
- nsCOMPtr<nsIWebBrowserPersist> wbp =
- do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES |
- nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = AddToCurrentDownloads(dl);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Creates a cycle that will be broken when the download finishes
- dl->mCancelable = wbp;
- (void)wbp->SetProgressListener(dl);
-
- // referrer policy can be anything since referrer is nullptr
- rv = wbp->SavePrivacyAwareURI(dl->mSource, nullptr,
- nullptr, mozilla::net::RP_Unset,
- nullptr, nullptr,
- dl->mTarget, dl->mPrivate);
- if (NS_FAILED(rv)) {
- dl->mCancelable = nullptr;
- (void)wbp->SetProgressListener(nullptr);
- return rv;
- }
-
- return NS_OK;
-}
-
-static nsresult
-RemoveDownloadByGUID(const nsACString& aGUID, mozIStorageConnection* aDBConn)
-{
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "DELETE FROM moz_downloads "
- "WHERE guid = :guid"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
-
- return NS_OK;
-}
-
-nsresult
-nsDownloadManager::RemoveDownload(const nsACString& aGUID)
-{
- RefPtr<nsDownload> dl = FindDownload(aGUID);
- MOZ_ASSERT(!dl, "Can't call RemoveDownload on a download in progress!");
- if (dl)
- return NS_ERROR_FAILURE;
-
- nsresult rv = GetDownloadFromDB(aGUID, getter_AddRefs(dl));
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (dl->mPrivate) {
- RemoveDownloadByGUID(aGUID, mPrivateDBConn);
- } else {
- RemoveDownloadByGUID(aGUID, mDBConn);
- }
-
- return NotifyDownloadRemoval(dl);
-}
-
-NS_IMETHODIMP
-nsDownloadManager::RemoveDownload(uint32_t aID)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_WARNING("Using integer IDs without compat mode enabled");
-
- RefPtr<nsDownload> dl = FindDownload(aID);
- MOZ_ASSERT(!dl, "Can't call RemoveDownload on a download in progress!");
- if (dl)
- return NS_ERROR_FAILURE;
-
- nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<mozIStorageStatement> stmt;
- rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "DELETE FROM moz_downloads "
- "WHERE id = :id"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID); // unsigned; 64-bit to prevent overflow
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Notify the UI with the topic and download id
- return NotifyDownloadRemoval(dl);
-}
-
-nsresult
-nsDownloadManager::NotifyDownloadRemoval(nsDownload* aRemoved)
-{
- nsCOMPtr<nsISupportsPRUint32> id;
- nsCOMPtr<nsISupportsCString> guid;
- nsresult rv;
-
- // Only send an integer ID notification if the download is public.
- bool sendDeprecatedNotification = !(aRemoved && aRemoved->mPrivate);
-
- if (sendDeprecatedNotification && aRemoved) {
- id = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- uint32_t dlID;
- rv = aRemoved->GetId(&dlID);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = id->SetData(dlID);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- if (sendDeprecatedNotification) {
- mObserverService->NotifyObservers(id,
- "download-manager-remove-download",
- nullptr);
- }
-
- if (aRemoved) {
- guid = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString guidStr;
- rv = aRemoved->GetGuid(guidStr);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = guid->SetData(guidStr);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- mObserverService->NotifyObservers(guid,
- "download-manager-remove-download-guid",
- nullptr);
- return NS_OK;
-}
-
-static nsresult
-DoRemoveDownloadsByTimeframe(mozIStorageConnection* aDBConn,
- int64_t aStartTime,
- int64_t aEndTime)
-{
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "DELETE FROM moz_downloads "
- "WHERE startTime >= :startTime "
- "AND startTime <= :endTime "
- "AND state NOT IN (:downloading, :paused, :queued)"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Bind the times
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Bind the active states
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("paused"), nsIDownloadManager::DOWNLOAD_PAUSED);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Execute
- rv = stmt->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::RemoveDownloadsByTimeframe(int64_t aStartTime,
- int64_t aEndTime)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- nsresult rv = DoRemoveDownloadsByTimeframe(mDBConn, aStartTime, aEndTime);
- nsresult rv2 = DoRemoveDownloadsByTimeframe(mPrivateDBConn, aStartTime, aEndTime);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_SUCCESS(rv2, rv2);
-
- // Notify the UI with the topic and null subject to indicate "remove multiple"
- return NotifyDownloadRemoval(nullptr);
-}
-
-NS_IMETHODIMP
-nsDownloadManager::CleanUp()
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- return CleanUp(mDBConn);
-}
-
-NS_IMETHODIMP
-nsDownloadManager::CleanUpPrivate()
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- return CleanUp(mPrivateDBConn);
-}
-
-nsresult
-nsDownloadManager::CleanUp(mozIStorageConnection* aDBConn)
-{
- DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
- nsIDownloadManager::DOWNLOAD_FAILED,
- nsIDownloadManager::DOWNLOAD_CANCELED,
- nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
- nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
- nsIDownloadManager::DOWNLOAD_DIRTY };
-
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "DELETE FROM moz_downloads "
- "WHERE state = ? "
- "OR state = ? "
- "OR state = ? "
- "OR state = ? "
- "OR state = ? "
- "OR state = ?"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, rv);
- for (uint32_t i = 0; i < ArrayLength(states); ++i) {
- rv = stmt->BindInt32ByIndex(i, states[i]);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- rv = stmt->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Notify the UI with the topic and null subject to indicate "remove multiple"
- return NotifyDownloadRemoval(nullptr);
-}
-
-static nsresult
-DoGetCanCleanUp(mozIStorageConnection* aDBConn, bool *aResult)
-{
- // This method should never return anything but NS_OK for the benefit of
- // unwitting consumers.
-
- *aResult = false;
-
- DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
- nsIDownloadManager::DOWNLOAD_FAILED,
- nsIDownloadManager::DOWNLOAD_CANCELED,
- nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
- nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
- nsIDownloadManager::DOWNLOAD_DIRTY };
-
- nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
- "SELECT COUNT(*) "
- "FROM moz_downloads "
- "WHERE state = ? "
- "OR state = ? "
- "OR state = ? "
- "OR state = ? "
- "OR state = ? "
- "OR state = ?"), getter_AddRefs(stmt));
- NS_ENSURE_SUCCESS(rv, NS_OK);
- for (uint32_t i = 0; i < ArrayLength(states); ++i) {
- rv = stmt->BindInt32ByIndex(i, states[i]);
- NS_ENSURE_SUCCESS(rv, NS_OK);
- }
-
- bool moreResults; // We don't really care...
- rv = stmt->ExecuteStep(&moreResults);
- NS_ENSURE_SUCCESS(rv, NS_OK);
-
- int32_t count;
- rv = stmt->GetInt32(0, &count);
- NS_ENSURE_SUCCESS(rv, NS_OK);
-
- if (count > 0)
- *aResult = true;
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::GetCanCleanUp(bool *aResult)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- return DoGetCanCleanUp(mDBConn, aResult);
-}
-
-NS_IMETHODIMP
-nsDownloadManager::GetCanCleanUpPrivate(bool *aResult)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- return DoGetCanCleanUp(mPrivateDBConn, aResult);
-}
-
-NS_IMETHODIMP
-nsDownloadManager::PauseDownload(uint32_t aID)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_WARNING("Using integer IDs without compat mode enabled");
-
- nsDownload *dl = FindDownload(aID);
- if (!dl)
- return NS_ERROR_FAILURE;
-
- return dl->Pause();
-}
-
-NS_IMETHODIMP
-nsDownloadManager::ResumeDownload(uint32_t aID)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_WARNING("Using integer IDs without compat mode enabled");
-
- nsDownload *dl = FindDownload(aID);
- if (!dl)
- return NS_ERROR_FAILURE;
-
- return dl->Resume();
-}
-
-NS_IMETHODIMP
-nsDownloadManager::GetDBConnection(mozIStorageConnection **aDBConn)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_ADDREF(*aDBConn = mDBConn);
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::GetPrivateDBConnection(mozIStorageConnection **aDBConn)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- NS_ADDREF(*aDBConn = mPrivateDBConn);
-
- return NS_OK;
- }
-
-NS_IMETHODIMP
-nsDownloadManager::AddListener(nsIDownloadProgressListener *aListener)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- mListeners.AppendObject(aListener);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::AddPrivacyAwareListener(nsIDownloadProgressListener *aListener)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- mPrivacyAwareListeners.AppendObject(aListener);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::RemoveListener(nsIDownloadProgressListener *aListener)
-{
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- mListeners.RemoveObject(aListener);
- mPrivacyAwareListeners.RemoveObject(aListener);
- return NS_OK;
-}
-
-void
-nsDownloadManager::NotifyListenersOnDownloadStateChange(int16_t aOldState,
- nsDownload *aDownload)
-{
- for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
- mPrivacyAwareListeners[i]->OnDownloadStateChange(aOldState, aDownload);
- }
-
- // Only privacy-aware listeners should receive notifications about private
- // downloads, while non-privacy-aware listeners receive no sign they exist.
- if (aDownload->mPrivate) {
- return;
- }
-
- for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
- mListeners[i]->OnDownloadStateChange(aOldState, aDownload);
- }
-}
-
-void
-nsDownloadManager::NotifyListenersOnProgressChange(nsIWebProgress *aProgress,
- nsIRequest *aRequest,
- int64_t aCurSelfProgress,
- int64_t aMaxSelfProgress,
- int64_t aCurTotalProgress,
- int64_t aMaxTotalProgress,
- nsDownload *aDownload)
-{
- for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
- mPrivacyAwareListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
- aMaxSelfProgress, aCurTotalProgress,
- aMaxTotalProgress, aDownload);
- }
-
- // Only privacy-aware listeners should receive notifications about private
- // downloads, while non-privacy-aware listeners receive no sign they exist.
- if (aDownload->mPrivate) {
- return;
- }
-
- for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
- mListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
- aMaxSelfProgress, aCurTotalProgress,
- aMaxTotalProgress, aDownload);
- }
-}
-
-void
-nsDownloadManager::NotifyListenersOnStateChange(nsIWebProgress *aProgress,
- nsIRequest *aRequest,
- uint32_t aStateFlags,
- nsresult aStatus,
- nsDownload *aDownload)
-{
- for (int32_t i = mPrivacyAwareListeners.Count() - 1; i >= 0; --i) {
- mPrivacyAwareListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus,
- aDownload);
- }
-
- // Only privacy-aware listeners should receive notifications about private
- // downloads, while non-privacy-aware listeners receive no sign they exist.
- if (aDownload->mPrivate) {
- return;
- }
-
- for (int32_t i = mListeners.Count() - 1; i >= 0; --i) {
- mListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus,
- aDownload);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsINavHistoryObserver
-
-NS_IMETHODIMP
-nsDownloadManager::OnBeginUpdateBatch()
-{
- // This method in not normally invoked when mUseJSTransfer is enabled, however
- // we provide an extra check in case it is called manually by add-ons.
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- // We already have a transaction, so don't make another
- if (mHistoryTransaction)
- return NS_OK;
-
- // Start a transaction that commits when deleted
- mHistoryTransaction = new mozStorageTransaction(mDBConn, true);
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnEndUpdateBatch()
-{
- // Get rid of the transaction and cause it to commit
- mHistoryTransaction = nullptr;
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnVisit(nsIURI *aURI, int64_t aVisitID, PRTime aTime,
- int64_t aSessionID, int64_t aReferringID,
- uint32_t aTransitionType, const nsACString& aGUID,
- bool aHidden, uint32_t aVisitCount, uint32_t aTyped,
- const nsAString& aLastKnowntTitle)
-{
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnTitleChanged(nsIURI *aURI,
- const nsAString &aPageTitle,
- const nsACString &aGUID)
-{
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnFrecencyChanged(nsIURI* aURI,
- int32_t aNewFrecency,
- const nsACString& aGUID,
- bool aHidden,
- PRTime aLastVisitDate)
-{
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnManyFrecenciesChanged()
-{
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnDeleteURI(nsIURI *aURI,
- const nsACString& aGUID,
- uint16_t aReason)
-{
- // This method in not normally invoked when mUseJSTransfer is enabled, however
- // we provide an extra check in case it is called manually by add-ons.
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- nsresult rv = RemoveDownloadsForURI(mGetIdsForURIStatement, aURI);
- nsresult rv2 = RemoveDownloadsForURI(mGetPrivateIdsForURIStatement, aURI);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_SUCCESS(rv2, rv2);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnClearHistory()
-{
- return CleanUp();
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnPageChanged(nsIURI *aURI,
- uint32_t aChangedAttribute,
- const nsAString& aNewValue,
- const nsACString &aGUID)
-{
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownloadManager::OnDeleteVisits(nsIURI *aURI, PRTime aVisitTime,
- const nsACString& aGUID,
- uint16_t aReason, uint32_t aTransitionType)
-{
- // Don't bother removing downloads until the page is removed.
- return NS_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIObserver
-
-NS_IMETHODIMP
-nsDownloadManager::Observe(nsISupports *aSubject,
- const char *aTopic,
- const char16_t *aData)
-{
- // This method in not normally invoked when mUseJSTransfer is enabled, however
- // we provide an extra check in case it is called manually by add-ons.
- NS_ENSURE_STATE(!mUseJSTransfer);
-
- // We need to count the active public downloads that could be lost
- // by quitting, and add any active private ones as well, since per-window
- // private browsing may be active.
- int32_t currDownloadCount = mCurrentDownloads.Count();
-
- // If we don't need to cancel all the downloads on quit, only count the ones
- // that aren't resumable.
- if (GetQuitBehavior() != QUIT_AND_CANCEL) {
- for (int32_t i = currDownloadCount - 1; i >= 0; --i) {
- if (mCurrentDownloads[i]->IsResumable()) {
- currDownloadCount--;
- }
- }
-
- // We have a count of the public, non-resumable downloads. Now we need
- // to add the total number of private downloads, since they are in danger
- // of being lost.
- currDownloadCount += mCurrentPrivateDownloads.Count();
- }
-
- nsresult rv;
- if (strcmp(aTopic, "oncancel") == 0) {
- nsCOMPtr<nsIDownload> dl = do_QueryInterface(aSubject, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- dl->Cancel();
- } else if (strcmp(aTopic, "profile-before-change") == 0) {
- CloseAllDBs();
- } else if (strcmp(aTopic, "quit-application") == 0) {
- // Try to pause all downloads and, if appropriate, mark them as auto-resume
- // unless user has specified that downloads should be canceled
- enum QuitBehavior behavior = GetQuitBehavior();
- if (behavior != QUIT_AND_CANCEL)
- (void)PauseAllDownloads(bool(behavior != QUIT_AND_PAUSE));
-
- // Remove downloads to break cycles and cancel downloads
- (void)RemoveAllDownloads();
-
- // Now that active downloads have been canceled, remove all completed or
- // aborted downloads if the user's retention policy specifies it.
- if (GetRetentionBehavior() == 1)
- CleanUp();
- } else if (strcmp(aTopic, "quit-application-requested") == 0 &&
- currDownloadCount) {
- nsCOMPtr<nsISupportsPRBool> cancelDownloads =
- do_QueryInterface(aSubject, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-#ifndef XP_MACOSX
- ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
- u"quitCancelDownloadsAlertTitle",
- u"quitCancelDownloadsAlertMsgMultiple",
- u"quitCancelDownloadsAlertMsg",
- u"dontQuitButtonWin");
-#else
- ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
- u"quitCancelDownloadsAlertTitle",
- u"quitCancelDownloadsAlertMsgMacMultiple",
- u"quitCancelDownloadsAlertMsgMac",
- u"dontQuitButtonMac");
-#endif
- } else if (strcmp(aTopic, "offline-requested") == 0 && currDownloadCount) {
- nsCOMPtr<nsISupportsPRBool> cancelDownloads =
- do_QueryInterface(aSubject, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
- u"offlineCancelDownloadsAlertTitle",
- u"offlineCancelDownloadsAlertMsgMultiple",
- u"offlineCancelDownloadsAlertMsg",
- u"dontGoOfflineButton");
- }
- else if (strcmp(aTopic, NS_IOSERVICE_GOING_OFFLINE_TOPIC) == 0) {
- // Pause all downloads, and mark them to auto-resume.
- (void)PauseAllDownloads(true);
- }
- else if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0 &&
- nsDependentString(aData).EqualsLiteral(NS_IOSERVICE_ONLINE)) {
- // We can now resume all downloads that are supposed to auto-resume.
- (void)ResumeAllDownloads(false);
- }
- else if (strcmp(aTopic, "alertclickcallback") == 0) {
- nsCOMPtr<nsIDownloadManagerUI> dmui =
- do_GetService("@mozilla.org/download-manager-ui;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- return dmui->Show(nullptr, nullptr, nsIDownloadManagerUI::REASON_USER_INTERACTED,
- aData && NS_strcmp(aData, u"private") == 0);
- } else if (strcmp(aTopic, "sleep_notification") == 0 ||
- strcmp(aTopic, "suspend_process_notification") == 0) {
- // Pause downloads if we're sleeping, and mark the downloads as auto-resume
- (void)PauseAllDownloads(true);
- } else if (strcmp(aTopic, "wake_notification") == 0 ||
- strcmp(aTopic, "resume_process_notification") == 0) {
- int32_t resumeOnWakeDelay = 10000;
- nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (pref)
- (void)pref->GetIntPref(PREF_BDM_RESUMEONWAKEDELAY, &resumeOnWakeDelay);
-
- // Wait a little bit before trying to resume to avoid resuming when network
- // connections haven't restarted yet
- mResumeOnWakeTimer = do_CreateInstance("@mozilla.org/timer;1");
- if (resumeOnWakeDelay >= 0 && mResumeOnWakeTimer) {
- (void)mResumeOnWakeTimer->InitWithFuncCallback(ResumeOnWakeCallback,
- this, resumeOnWakeDelay, nsITimer::TYPE_ONE_SHOT);
- }
- } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
- // Upon leaving private browsing mode, cancel all private downloads,
- // remove all trace of them, and then blow away the private database
- // and recreate a blank one.
- RemoveAllDownloads(mCurrentPrivateDownloads);
- InitPrivateDB();
- } else if (strcmp(aTopic, "last-pb-context-exiting") == 0) {
- // If there are active private downloads, prompt the user to confirm leaving
- // private browsing mode (thereby cancelling them). Otherwise, silently proceed.
- if (!mCurrentPrivateDownloads.Count())
- return NS_OK;
-
- nsCOMPtr<nsISupportsPRBool> cancelDownloads = do_QueryInterface(aSubject, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- ConfirmCancelDownloads(mCurrentPrivateDownloads.Count(), cancelDownloads,
- u"leavePrivateBrowsingCancelDownloadsAlertTitle",
- u"leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple2",
- u"leavePrivateBrowsingWindowsCancelDownloadsAlertMsg2",
- u"dontLeavePrivateBrowsingButton2");
- }
-
- return NS_OK;
-}
-
-void
-nsDownloadManager::ConfirmCancelDownloads(int32_t aCount,
- nsISupportsPRBool *aCancelDownloads,
- const char16_t *aTitle,
- const char16_t *aCancelMessageMultiple,
- const char16_t *aCancelMessageSingle,
- const char16_t *aDontCancelButton)
-{
- // If user has already dismissed quit request, then do nothing
- bool quitRequestCancelled = false;
- aCancelDownloads->GetData(&quitRequestCancelled);
- if (quitRequestCancelled)
- return;
-
- nsXPIDLString title, message, quitButton, dontQuitButton;
-
- mBundle->GetStringFromName(aTitle, getter_Copies(title));
-
- nsAutoString countString;
- countString.AppendInt(aCount);
- const char16_t *strings[1] = { countString.get() };
- if (aCount > 1) {
- mBundle->FormatStringFromName(aCancelMessageMultiple, strings, 1,
- getter_Copies(message));
- mBundle->FormatStringFromName(u"cancelDownloadsOKTextMultiple",
- strings, 1, getter_Copies(quitButton));
- } else {
- mBundle->GetStringFromName(aCancelMessageSingle, getter_Copies(message));
- mBundle->GetStringFromName(u"cancelDownloadsOKText",
- getter_Copies(quitButton));
- }
-
- mBundle->GetStringFromName(aDontCancelButton, getter_Copies(dontQuitButton));
-
- // Get Download Manager window, to be parent of alert.
- nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
- nsCOMPtr<mozIDOMWindowProxy> dmWindow;
- if (wm) {
- wm->GetMostRecentWindow(u"Download:Manager",
- getter_AddRefs(dmWindow));
- }
-
- // Show alert.
- nsCOMPtr<nsIPromptService> prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
- if (prompter) {
- int32_t flags = (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_0) + (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1);
- bool nothing = false;
- int32_t button;
- prompter->ConfirmEx(dmWindow, title, message, flags, quitButton.get(), dontQuitButton.get(), nullptr, nullptr, ¬hing, &button);
-
- aCancelDownloads->SetData(button == 1);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsDownload
-
-NS_IMPL_CLASSINFO(nsDownload, nullptr, 0, NS_DOWNLOAD_CID)
-NS_IMPL_ISUPPORTS_CI(
- nsDownload
- , nsIDownload
- , nsITransfer
- , nsIWebProgressListener
- , nsIWebProgressListener2
-)
-
-nsDownload::nsDownload() : mDownloadState(nsIDownloadManager::DOWNLOAD_NOTSTARTED),
- mID(0),
- mPercentComplete(0),
- mCurrBytes(0),
- mMaxBytes(-1),
- mStartTime(0),
- mLastUpdate(PR_Now() - (uint32_t)gUpdateInterval),
- mResumedAt(-1),
- mSpeed(0),
- mHasMultipleFiles(false),
- mPrivate(false),
- mAutoResume(DONT_RESUME)
-{
-}
-
-nsDownload::~nsDownload()
-{
-}
-
-NS_IMETHODIMP nsDownload::SetSha256Hash(const nsACString& aHash) {
- MOZ_ASSERT(NS_IsMainThread(), "Must call SetSha256Hash on main thread");
- // This will be used later to query the application reputation service.
- mHash = aHash;
- return NS_OK;
-}
-
-NS_IMETHODIMP nsDownload::SetSignatureInfo(nsIArray* aSignatureInfo) {
- MOZ_ASSERT(NS_IsMainThread(), "Must call SetSignatureInfo on main thread");
- // This will be used later to query the application reputation service.
- mSignatureInfo = aSignatureInfo;
- return NS_OK;
-}
-
-NS_IMETHODIMP nsDownload::SetRedirects(nsIArray* aRedirects) {
- MOZ_ASSERT(NS_IsMainThread(), "Must call SetRedirects on main thread");
- // This will be used later to query the application reputation service.
- mRedirects = aRedirects;
- return NS_OK;
-}
-
-#ifdef MOZ_WIDGET_GTK
-static void gio_set_metadata_done(GObject *source_obj, GAsyncResult *res, gpointer user_data)
-{
- GError *err = nullptr;
- g_file_set_attributes_finish(G_FILE(source_obj), res, nullptr, &err);
- if (err) {
-#ifdef DEBUG
- NS_DebugBreak(NS_DEBUG_WARNING, "Set file metadata failed: ", err->message, __FILE__, __LINE__);
-#endif
- g_error_free(err);
- }
-}
-#endif
-
-nsresult
-nsDownload::SetState(DownloadState aState)
-{
- NS_ASSERTION(mDownloadState != aState,
- "Trying to set the download state to what it already is set to!");
-
- int16_t oldState = mDownloadState;
- mDownloadState = aState;
-
- // We don't want to lose access to our member variables
- RefPtr<nsDownload> kungFuDeathGrip = this;
-
- // When the state changed listener is dispatched, queries to the database and
- // the download manager api should reflect what the nsIDownload object would
- // return. So, if a download is done (finished, canceled, etc.), it should
- // first be removed from the current downloads. We will also have to update
- // the database *before* notifying listeners. At this point, you can safely
- // dispatch to the observers as well.
- switch (aState) {
- case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
- case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
- case nsIDownloadManager::DOWNLOAD_DIRTY:
- case nsIDownloadManager::DOWNLOAD_CANCELED:
- case nsIDownloadManager::DOWNLOAD_FAILED:
-#ifdef ANDROID
- // If we still have a temp file, remove it
- bool tempExists;
- if (mTempFile && NS_SUCCEEDED(mTempFile->Exists(&tempExists)) && tempExists) {
- nsresult rv = mTempFile->Remove(false);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-#endif
-
- // Transfers are finished, so break the reference cycle
- Finalize();
- break;
-#ifdef DOWNLOAD_SCANNER
- case nsIDownloadManager::DOWNLOAD_SCANNING:
- {
- nsresult rv = mDownloadManager->mScanner ? mDownloadManager->mScanner->ScanDownload(this) : NS_ERROR_NOT_INITIALIZED;
- // If we failed, then fall through to 'download finished'
- if (NS_SUCCEEDED(rv))
- break;
- mDownloadState = aState = nsIDownloadManager::DOWNLOAD_FINISHED;
- }
-#endif
- case nsIDownloadManager::DOWNLOAD_FINISHED:
- {
- nsresult rv = ExecuteDesiredAction();
- if (NS_FAILED(rv)) {
- // We've failed to execute the desired action. As a result, we should
- // fail the download so the user can try again.
- (void)FailDownload(rv, nullptr);
- return rv;
- }
-
- // Now that we're done with handling the download, clean it up
- Finalize();
-
- nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
-
- // Master pref to control this function.
- bool showTaskbarAlert = true;
- if (pref)
- pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert);
-
- if (showTaskbarAlert) {
- int32_t alertInterval = 2000;
- if (pref)
- pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
-
- int64_t alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC;
- int64_t goat = PR_Now() - mStartTime;
- showTaskbarAlert = goat > alertIntervalUSec;
-
- int32_t size = mPrivate ?
- mDownloadManager->mCurrentPrivateDownloads.Count() :
- mDownloadManager->mCurrentDownloads.Count();
- if (showTaskbarAlert && size == 0) {
- nsCOMPtr<nsIAlertsService> alerts =
- do_GetService("@mozilla.org/alerts-service;1");
- if (alerts) {
- nsXPIDLString title, message;
-
- mDownloadManager->mBundle->GetStringFromName(
- u"downloadsCompleteTitle",
- getter_Copies(title));
- mDownloadManager->mBundle->GetStringFromName(
- u"downloadsCompleteMsg",
- getter_Copies(message));
-
- bool removeWhenDone =
- mDownloadManager->GetRetentionBehavior() == 0;
-
- // If downloads are automatically removed per the user's
- // retention policy, there's no reason to make the text clickable
- // because if it is, they'll click open the download manager and
- // the items they downloaded will have been removed.
- alerts->ShowAlertNotification(
- NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title,
- message, !removeWhenDone,
- mPrivate ? NS_LITERAL_STRING("private") : NS_LITERAL_STRING("non-private"),
- mDownloadManager, EmptyString(), NS_LITERAL_STRING("auto"),
- EmptyString(), EmptyString(), nullptr, mPrivate,
- false /* requireInteraction */);
- }
- }
- }
-
-#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK)
- nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget);
- nsCOMPtr<nsIFile> file;
- nsAutoString path;
-
- if (fileURL &&
- NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
- file &&
- NS_SUCCEEDED(file->GetPath(path))) {
-
-#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
- // On Windows and Gtk, add the download to the system's "recent documents"
- // list, with a pref to disable.
- {
- bool addToRecentDocs = true;
- if (pref)
- pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
-#ifdef MOZ_WIDGET_ANDROID
- if (addToRecentDocs) {
- nsCOMPtr<nsIMIMEInfo> mimeInfo;
- nsAutoCString contentType;
- GetMIMEInfo(getter_AddRefs(mimeInfo));
-
- if (mimeInfo)
- mimeInfo->GetMIMEType(contentType);
-
- if (jni::IsFennec()) {
- java::DownloadsIntegration::ScanMedia(path, NS_ConvertUTF8toUTF16(contentType));
- }
- }
-#else
- if (addToRecentDocs && !mPrivate) {
-#ifdef XP_WIN
- ::SHAddToRecentDocs(SHARD_PATHW, path.get());
-#elif defined(MOZ_WIDGET_GTK)
- GtkRecentManager* manager = gtk_recent_manager_get_default();
-
- gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(),
- nullptr, nullptr);
- if (uri) {
- gtk_recent_manager_add_item(manager, uri);
- g_free(uri);
- }
-#endif
- }
-#endif
-#ifdef MOZ_WIDGET_GTK
- // Use GIO to store the source URI for later display in the file manager.
- GFile* gio_file = g_file_new_for_path(NS_ConvertUTF16toUTF8(path).get());
- nsCString source_uri;
- rv = mSource->GetSpec(source_uri);
- NS_ENSURE_SUCCESS(rv, rv);
- GFileInfo *file_info = g_file_info_new();
- g_file_info_set_attribute_string(file_info, "metadata::download-uri", source_uri.get());
- g_file_set_attributes_async(gio_file,
- file_info,
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_DEFAULT,
- nullptr, gio_set_metadata_done, nullptr);
- g_object_unref(file_info);
- g_object_unref(gio_file);
-#endif
- }
-#endif
-
-#ifdef XP_MACOSX
- // On OS X, make the downloads stack bounce.
- CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault,
- NS_ConvertUTF16toUTF8(path).get(),
- kCFStringEncodingUTF8);
- CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
- ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
- observedObject, nullptr, TRUE);
- ::CFRelease(observedObject);
-#endif
- }
-
-#ifdef XP_WIN
- // Adjust file attributes so that by default, new files are indexed
- // by desktop search services. Skip off those that land in the temp
- // folder.
- nsCOMPtr<nsIFile> tempDir, fileDir;
- rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempDir));
- NS_ENSURE_SUCCESS(rv, rv);
- (void)file->GetParent(getter_AddRefs(fileDir));
-
- bool isTemp = false;
- if (fileDir)
- (void)fileDir->Equals(tempDir, &isTemp);
-
- nsCOMPtr<nsILocalFileWin> localFileWin(do_QueryInterface(file));
- if (!isTemp && localFileWin)
- (void)localFileWin->SetFileAttributesWin(nsILocalFileWin::WFA_SEARCH_INDEXED);
-#endif
-
-#endif
- // Now remove the download if the user's retention policy is "Remove when Done"
- if (mDownloadManager->GetRetentionBehavior() == 0)
- mDownloadManager->RemoveDownload(mGUID);
- }
- break;
- default:
- break;
- }
-
- // Before notifying the listener, we must update the database so that calls
- // to it work out properly.
- nsresult rv = UpdateDB();
- NS_ENSURE_SUCCESS(rv, rv);
-
- mDownloadManager->NotifyListenersOnDownloadStateChange(oldState, this);
-
- switch (mDownloadState) {
- case nsIDownloadManager::DOWNLOAD_DOWNLOADING:
- // Only send the dl-start event to downloads that are actually starting.
- if (oldState == nsIDownloadManager::DOWNLOAD_QUEUED) {
- if (!mPrivate)
- mDownloadManager->SendEvent(this, "dl-start");
- }
- break;
- case nsIDownloadManager::DOWNLOAD_FAILED:
- if (!mPrivate)
- mDownloadManager->SendEvent(this, "dl-failed");
- break;
- case nsIDownloadManager::DOWNLOAD_SCANNING:
- if (!mPrivate)
- mDownloadManager->SendEvent(this, "dl-scanning");
- break;
- case nsIDownloadManager::DOWNLOAD_FINISHED:
- if (!mPrivate)
- mDownloadManager->SendEvent(this, "dl-done");
- break;
- case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
- case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
- if (!mPrivate)
- mDownloadManager->SendEvent(this, "dl-blocked");
- break;
- case nsIDownloadManager::DOWNLOAD_DIRTY:
- if (!mPrivate)
- mDownloadManager->SendEvent(this, "dl-dirty");
- break;
- case nsIDownloadManager::DOWNLOAD_CANCELED:
- if (!mPrivate)
- mDownloadManager->SendEvent(this, "dl-cancel");
- break;
- default:
- break;
- }
- return NS_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIWebProgressListener2
-
-NS_IMETHODIMP
-nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
- nsIRequest *aRequest,
- int64_t aCurSelfProgress,
- int64_t aMaxSelfProgress,
- int64_t aCurTotalProgress,
- int64_t aMaxTotalProgress)
-{
- if (!mRequest)
- mRequest = aRequest; // used for pause/resume
-
- if (mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED) {
- // Obtain the referrer
- nsresult rv;
- nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
- nsCOMPtr<nsIURI> referrer = mReferrer;
- if (channel)
- (void)NS_GetReferrerFromChannel(channel, getter_AddRefs(mReferrer));
-
- // Restore the original referrer if the new one isn't useful
- if (!mReferrer)
- mReferrer = referrer;
-
- // If we have a MIME info, we know that exthandler has already added this to
- // the history, but if we do not, we'll have to add it ourselves.
- if (!mMIMEInfo && !mPrivate) {
- nsCOMPtr<nsIDownloadHistory> dh =
- do_GetService(NS_DOWNLOADHISTORY_CONTRACTID);
- if (dh)
- (void)dh->AddDownload(mSource, mReferrer, mStartTime, mTarget);
- }
-
- // Fetch the entityID, but if we can't get it, don't panic (non-resumable)
- nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(aRequest));
- if (resumableChannel)
- (void)resumableChannel->GetEntityID(mEntityID);
-
- // Before we update the state and dispatch state notifications, we want to
- // ensure that we have the correct state for this download with regards to
- // its percent completion and size.
- SetProgressBytes(0, aMaxTotalProgress);
-
- // Update the state and the database
- rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- // filter notifications since they come in so frequently
- PRTime now = PR_Now();
- PRIntervalTime delta = now - mLastUpdate;
- if (delta < gUpdateInterval)
- return NS_OK;
-
- mLastUpdate = now;
-
- // Calculate the speed using the elapsed delta time and bytes downloaded
- // during that time for more accuracy.
- double elapsedSecs = double(delta) / PR_USEC_PER_SEC;
- if (elapsedSecs > 0) {
- double speed = double(aCurTotalProgress - mCurrBytes) / elapsedSecs;
- if (mCurrBytes == 0) {
- mSpeed = speed;
- } else {
- // Calculate 'smoothed average' of 10 readings.
- mSpeed = mSpeed * 0.9 + speed * 0.1;
- }
- }
-
- SetProgressBytes(aCurTotalProgress, aMaxTotalProgress);
-
- // Report to the listener our real sizes
- int64_t currBytes, maxBytes;
- (void)GetAmountTransferred(&currBytes);
- (void)GetSize(&maxBytes);
- mDownloadManager->NotifyListenersOnProgressChange(
- aWebProgress, aRequest, currBytes, maxBytes, currBytes, maxBytes, this);
-
- // If the maximums are different, then there must be more than one file
- if (aMaxSelfProgress != aMaxTotalProgress)
- mHasMultipleFiles = true;
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::OnRefreshAttempted(nsIWebProgress *aWebProgress,
- nsIURI *aUri,
- int32_t aDelay,
- bool aSameUri,
- bool *allowRefresh)
-{
- *allowRefresh = true;
- return NS_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIWebProgressListener
-
-NS_IMETHODIMP
-nsDownload::OnProgressChange(nsIWebProgress *aWebProgress,
- nsIRequest *aRequest,
- int32_t aCurSelfProgress,
- int32_t aMaxSelfProgress,
- int32_t aCurTotalProgress,
- int32_t aMaxTotalProgress)
-{
- return OnProgressChange64(aWebProgress, aRequest,
- aCurSelfProgress, aMaxSelfProgress,
- aCurTotalProgress, aMaxTotalProgress);
-}
-
-NS_IMETHODIMP
-nsDownload::OnLocationChange(nsIWebProgress *aWebProgress,
- nsIRequest *aRequest, nsIURI *aLocation,
- uint32_t aFlags)
-{
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
- nsIRequest *aRequest, nsresult aStatus,
- const char16_t *aMessage)
-{
- if (NS_FAILED(aStatus))
- return FailDownload(aStatus, aMessage);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::OnStateChange(nsIWebProgress *aWebProgress,
- nsIRequest *aRequest, uint32_t aStateFlags,
- nsresult aStatus)
-{
- MOZ_ASSERT(NS_IsMainThread(), "Must call OnStateChange in main thread");
-
- // We don't want to lose access to our member variables
- RefPtr<nsDownload> kungFuDeathGrip = this;
-
- // Check if we're starting a request; the NETWORK flag is necessary to not
- // pick up the START of *each* file but only for the whole request
- if ((aStateFlags & STATE_START) && (aStateFlags & STATE_IS_NETWORK)) {
- nsresult rv;
- nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
- if (NS_SUCCEEDED(rv)) {
- uint32_t status;
- rv = channel->GetResponseStatus(&status);
- // HTTP 450 - Blocked by parental control proxies
- if (NS_SUCCEEDED(rv) && status == 450) {
- // Cancel using the provided object
- (void)Cancel();
-
- // Fail the download
- (void)SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL);
- }
- }
- } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK) &&
- IsFinishable()) {
- // We got both STOP and NETWORK so that means the whole request is done
- // (and not just a single file if there are multiple files)
- if (NS_SUCCEEDED(aStatus)) {
- // We can't completely trust the bytes we've added up because we might be
- // missing on some/all of the progress updates (especially from cache).
- // Our best bet is the file itself, but if for some reason it's gone or
- // if we have multiple files, the next best is what we've calculated.
- int64_t fileSize;
- nsCOMPtr<nsIFile> file;
- // We need a nsIFile clone to deal with file size caching issues. :(
- nsCOMPtr<nsIFile> clone;
- if (!mHasMultipleFiles &&
- NS_SUCCEEDED(GetTargetFile(getter_AddRefs(file))) &&
- NS_SUCCEEDED(file->Clone(getter_AddRefs(clone))) &&
- NS_SUCCEEDED(clone->GetFileSize(&fileSize)) && fileSize > 0) {
- mCurrBytes = mMaxBytes = fileSize;
-
- // If we resumed, keep the fact that we did and fix size calculations
- if (WasResumed())
- mResumedAt = 0;
- } else if (mMaxBytes == -1) {
- mMaxBytes = mCurrBytes;
- } else {
- mCurrBytes = mMaxBytes;
- }
-
- mPercentComplete = 100;
- mLastUpdate = PR_Now();
-
-#ifdef DOWNLOAD_SCANNER
- bool scan = true;
- nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
- if (prefs)
- (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan);
-
- if (scan)
- (void)SetState(nsIDownloadManager::DOWNLOAD_SCANNING);
- else
- (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
-#else
- (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
-#endif
- } else {
- // We failed for some unknown reason -- fail with a generic message
- (void)FailDownload(aStatus, nullptr);
- }
- }
-
- mDownloadManager->NotifyListenersOnStateChange(aWebProgress, aRequest,
- aStateFlags, aStatus, this);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::OnSecurityChange(nsIWebProgress *aWebProgress,
- nsIRequest *aRequest, uint32_t aState)
-{
- return NS_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIDownload
-
-NS_IMETHODIMP
-nsDownload::Init(nsIURI *aSource,
- nsIURI *aTarget,
- const nsAString& aDisplayName,
- nsIMIMEInfo *aMIMEInfo,
- PRTime aStartTime,
- nsIFile *aTempFile,
- nsICancelable *aCancelable,
- bool aIsPrivate)
-{
- NS_WARNING("Huh...how did we get here?!");
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetState(int16_t *aState)
-{
- *aState = mDownloadState;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetDisplayName(nsAString &aDisplayName)
-{
- aDisplayName = mDisplayName;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetCancelable(nsICancelable **aCancelable)
-{
- *aCancelable = mCancelable;
- NS_IF_ADDREF(*aCancelable);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetTarget(nsIURI **aTarget)
-{
- *aTarget = mTarget;
- NS_IF_ADDREF(*aTarget);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetSource(nsIURI **aSource)
-{
- *aSource = mSource;
- NS_IF_ADDREF(*aSource);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetStartTime(int64_t *aStartTime)
-{
- *aStartTime = mStartTime;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetPercentComplete(int32_t *aPercentComplete)
-{
- *aPercentComplete = mPercentComplete;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetAmountTransferred(int64_t *aAmountTransferred)
-{
- *aAmountTransferred = mCurrBytes + (WasResumed() ? mResumedAt : 0);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetSize(int64_t *aSize)
-{
- *aSize = mMaxBytes + (WasResumed() && mMaxBytes != -1 ? mResumedAt : 0);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetMIMEInfo(nsIMIMEInfo **aMIMEInfo)
-{
- *aMIMEInfo = mMIMEInfo;
- NS_IF_ADDREF(*aMIMEInfo);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetTargetFile(nsIFile **aTargetFile)
-{
- nsresult rv;
-
- nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
- if (NS_FAILED(rv)) {
- return rv;
- }
-
- nsCOMPtr<nsIFile> file;
- rv = fileURL->GetFile(getter_AddRefs(file));
- if (NS_FAILED(rv)) {
- return rv;
- }
-
- file.forget(aTargetFile);
- return rv;
-}
-
-NS_IMETHODIMP
-nsDownload::GetSpeed(double *aSpeed)
-{
- *aSpeed = mSpeed;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetId(uint32_t *aId)
-{
- if (mPrivate) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- *aId = mID;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetGuid(nsACString &aGUID)
-{
- aGUID = mGUID;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetReferrer(nsIURI **referrer)
-{
- NS_IF_ADDREF(*referrer = mReferrer);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetResumable(bool *resumable)
-{
- *resumable = IsResumable();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::GetIsPrivate(bool *isPrivate)
-{
- *isPrivate = mPrivate;
- return NS_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsDownload Helper Functions
-
-void
-nsDownload::Finalize()
-{
- // We're stopping, so break the cycle we created at download start
- mCancelable = nullptr;
-
- // Reset values that aren't needed anymore, so the DB can be updated as well
- mEntityID.Truncate();
- mTempFile = nullptr;
-
- // Remove ourself from the active downloads
- nsCOMArray<nsDownload>& currentDownloads = mPrivate ?
- mDownloadManager->mCurrentPrivateDownloads :
- mDownloadManager->mCurrentDownloads;
- (void)currentDownloads.RemoveObject(this);
-
- // Make sure we do not automatically resume
- mAutoResume = DONT_RESUME;
-}
-
-nsresult
-nsDownload::ExecuteDesiredAction()
-{
- // nsExternalHelperAppHandler is the only caller of AddDownload that sets a
- // tempfile parameter. In this case, execute the desired action according to
- // the saved mime info.
- if (!mTempFile) {
- return NS_OK;
- }
-
- // We need to bail if for some reason the temp file got removed
- bool fileExists;
- if (NS_FAILED(mTempFile->Exists(&fileExists)) || !fileExists)
- return NS_ERROR_FILE_NOT_FOUND;
-
- // Assume an unknown action is save to disk
- nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
- if (mMIMEInfo) {
- nsresult rv = mMIMEInfo->GetPreferredAction(&action);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- nsresult rv = NS_OK;
- switch (action) {
- case nsIMIMEInfo::saveToDisk:
- // Move the file to the proper location
- rv = MoveTempToTarget();
- if (NS_SUCCEEDED(rv)) {
- rv = FixTargetPermissions();
- }
- break;
- case nsIMIMEInfo::useHelperApp:
- case nsIMIMEInfo::useSystemDefault:
- // For these cases we have to move the file to the target location and
- // open with the appropriate application
- rv = OpenWithApplication();
- break;
- default:
- break;
- }
-
- return rv;
-}
-
-nsresult
-nsDownload::FixTargetPermissions()
-{
- nsCOMPtr<nsIFile> target;
- nsresult rv = GetTargetFile(getter_AddRefs(target));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Set perms according to umask.
- nsCOMPtr<nsIPropertyBag2> infoService =
- do_GetService("@mozilla.org/system-info;1");
- uint32_t gUserUmask = 0;
- rv = infoService->GetPropertyAsUint32(NS_LITERAL_STRING("umask"),
- &gUserUmask);
- if (NS_SUCCEEDED(rv)) {
- (void)target->SetPermissions(0666 & ~gUserUmask);
- }
- return NS_OK;
-}
-
-nsresult
-nsDownload::MoveTempToTarget()
-{
- nsCOMPtr<nsIFile> target;
- nsresult rv = GetTargetFile(getter_AddRefs(target));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // MoveTo will fail if the file already exists, but we've already obtained
- // confirmation from the user that this is OK, so remove it if it exists.
- bool fileExists;
- if (NS_SUCCEEDED(target->Exists(&fileExists)) && fileExists) {
- rv = target->Remove(false);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- // Extract the new leaf name from the file location
- nsAutoString fileName;
- rv = target->GetLeafName(fileName);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIFile> dir;
- rv = target->GetParent(getter_AddRefs(dir));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = mTempFile->MoveTo(dir, fileName);
- return rv;
-}
-
-nsresult
-nsDownload::OpenWithApplication()
-{
- // First move the temporary file to the target location
- nsCOMPtr<nsIFile> target;
- nsresult rv = GetTargetFile(getter_AddRefs(target));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Move the temporary file to the target location
- rv = MoveTempToTarget();
- NS_ENSURE_SUCCESS(rv, rv);
-
- bool deleteTempFileOnExit;
- nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
- if (!prefs || NS_FAILED(prefs->GetBoolPref(PREF_BH_DELETETEMPFILEONEXIT,
- &deleteTempFileOnExit))) {
- // No prefservice or no pref set; use default value
-#if !defined(XP_MACOSX)
- // Mac users have been very verbal about temp files being deleted on
- // app exit - they don't like it - but we'll continue to do this on
- // other platforms for now.
- deleteTempFileOnExit = true;
-#else
- deleteTempFileOnExit = false;
-#endif
- }
-
- // Always schedule files to be deleted at the end of the private browsing
- // mode, regardless of the value of the pref.
- if (deleteTempFileOnExit || mPrivate) {
-
- // Make the tmp file readonly so users won't lose changes.
- target->SetPermissions(0400);
-
- // Use the ExternalHelperAppService to push the temporary file to the list
- // of files to be deleted on exit.
- nsCOMPtr<nsPIExternalAppLauncher> appLauncher(do_GetService
- (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID));
-
- // Even if we are unable to get this service we return the result
- // of LaunchWithFile() which makes more sense.
- if (appLauncher) {
- if (mPrivate) {
- (void)appLauncher->DeleteTemporaryPrivateFileWhenPossible(target);
- } else {
- (void)appLauncher->DeleteTemporaryFileOnExit(target);
- }
- }
- }
-
- return mMIMEInfo->LaunchWithFile(target);
-}
-
-void
-nsDownload::SetStartTime(int64_t aStartTime)
-{
- mStartTime = aStartTime;
- mLastUpdate = aStartTime;
-}
-
-void
-nsDownload::SetProgressBytes(int64_t aCurrBytes, int64_t aMaxBytes)
-{
- mCurrBytes = aCurrBytes;
- mMaxBytes = aMaxBytes;
-
- // Get the real bytes that include resume position
- int64_t currBytes, maxBytes;
- (void)GetAmountTransferred(&currBytes);
- (void)GetSize(&maxBytes);
-
- if (currBytes == maxBytes)
- mPercentComplete = 100;
- else if (maxBytes <= 0)
- mPercentComplete = -1;
- else
- mPercentComplete = (int32_t)((double)currBytes / maxBytes * 100 + .5);
-}
-
-NS_IMETHODIMP
-nsDownload::Pause()
-{
- if (!IsResumable())
- return NS_ERROR_UNEXPECTED;
-
- nsresult rv = CancelTransfer();
- NS_ENSURE_SUCCESS(rv, rv);
-
- return SetState(nsIDownloadManager::DOWNLOAD_PAUSED);
-}
-
-nsresult
-nsDownload::CancelTransfer()
-{
- nsresult rv = NS_OK;
- if (mCancelable) {
- rv = mCancelable->Cancel(NS_BINDING_ABORTED);
- // we're done with this, so break the cycle
- mCancelable = nullptr;
- }
-
- return rv;
-}
-
-NS_IMETHODIMP
-nsDownload::Cancel()
-{
- // Don't cancel if download is already finished
- if (IsFinished())
- return NS_OK;
-
- // Have the download cancel its connection
- (void)CancelTransfer();
-
- // Dump the temp file because we know we don't need the file anymore. The
- // underlying transfer creating the file doesn't delete the file because it
- // can't distinguish between a pause that cancels the transfer or a real
- // cancel.
- if (mTempFile) {
- bool exists;
- mTempFile->Exists(&exists);
- if (exists)
- mTempFile->Remove(false);
- }
-
- nsCOMPtr<nsIFile> file;
- if (NS_SUCCEEDED(GetTargetFile(getter_AddRefs(file))))
- {
- bool exists;
- file->Exists(&exists);
- if (exists)
- file->Remove(false);
- }
-
- nsresult rv = SetState(nsIDownloadManager::DOWNLOAD_CANCELED);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDownload::Resume()
-{
- if (!IsPaused() || !IsResumable())
- return NS_ERROR_UNEXPECTED;
-
- nsresult rv;
- nsCOMPtr<nsIWebBrowserPersist> wbp =
- do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE |
- nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Create a new channel for the source URI
- nsCOMPtr<nsIChannel> channel;
- nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(wbp));
- rv = NS_NewChannel(getter_AddRefs(channel),
- mSource,
- nsContentUtils::GetSystemPrincipal(),
- nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
- nsIContentPolicy::TYPE_OTHER,
- nullptr, // aLoadGroup
- ir);
-
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
- if (pbChannel) {
- pbChannel->SetPrivate(mPrivate);
- }
-
- // Make sure we can get a file, either the temporary or the real target, for
- // both purposes of file size and a target to write to
- nsCOMPtr<nsIFile> targetLocalFile(mTempFile);
- if (!targetLocalFile) {
- rv = GetTargetFile(getter_AddRefs(targetLocalFile));
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- // Get the file size to be used as an offset, but if anything goes wrong
- // along the way, we'll silently restart at 0.
- int64_t fileSize;
- // We need a nsIFile clone to deal with file size caching issues. :(
- nsCOMPtr<nsIFile> clone;
- if (NS_FAILED(targetLocalFile->Clone(getter_AddRefs(clone))) ||
- NS_FAILED(clone->GetFileSize(&fileSize)))
- fileSize = 0;
-
- // Set the channel to resume at the right position along with the entityID
- nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(channel));
- if (!resumableChannel)
- return NS_ERROR_UNEXPECTED;
- rv = resumableChannel->ResumeAt(fileSize, mEntityID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // If we know the max size, we know what it should be when resuming
- int64_t maxBytes;
- GetSize(&maxBytes);
- SetProgressBytes(0, maxBytes != -1 ? maxBytes - fileSize : -1);
- // Track where we resumed because progress notifications restart at 0
- mResumedAt = fileSize;
-
- // Set the referrer
- if (mReferrer) {
- nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
- if (httpChannel) {
- rv = httpChannel->SetReferrer(mReferrer);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
-
- // Creates a cycle that will be broken when the download finishes
- mCancelable = wbp;
- (void)wbp->SetProgressListener(this);
-
- // Save the channel using nsIWBP
- rv = wbp->SaveChannel(channel, targetLocalFile);
- if (NS_FAILED(rv)) {
- mCancelable = nullptr;
- (void)wbp->SetProgressListener(nullptr);
- return rv;
- }
-
- return SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
-}
-
-NS_IMETHODIMP
-nsDownload::Remove()
-{
- return mDownloadManager->RemoveDownload(mGUID);
-}
-
-NS_IMETHODIMP
-nsDownload::Retry()
-{
- return mDownloadManager->RetryDownload(mGUID);
-}
-
-bool
-nsDownload::IsPaused()
-{
- return mDownloadState == nsIDownloadManager::DOWNLOAD_PAUSED;
-}
-
-bool
-nsDownload::IsResumable()
-{
- return !mEntityID.IsEmpty();
-}
-
-bool
-nsDownload::WasResumed()
-{
- return mResumedAt != -1;
-}
-
-bool
-nsDownload::ShouldAutoResume()
-{
- return mAutoResume == AUTO_RESUME;
-}
-
-bool
-nsDownload::IsFinishable()
-{
- return mDownloadState == nsIDownloadManager::DOWNLOAD_NOTSTARTED ||
- mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED ||
- mDownloadState == nsIDownloadManager::DOWNLOAD_DOWNLOADING;
-}
-
-bool
-nsDownload::IsFinished()
-{
- return mDownloadState == nsIDownloadManager::DOWNLOAD_FINISHED;
-}
-
-nsresult
-nsDownload::UpdateDB()
-{
- NS_ASSERTION(mID, "Download ID is stored as zero. This is bad!");
- NS_ASSERTION(mDownloadManager, "Egads! We have no download manager!");
-
- mozIStorageStatement *stmt = mPrivate ?
- mDownloadManager->mUpdatePrivateDownloadStatement : mDownloadManager->mUpdateDownloadStatement;
-
- nsAutoString tempPath;
- if (mTempFile)
- (void)mTempFile->GetPath(tempPath);
- nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), tempPath);
-
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), mStartTime);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), mLastUpdate);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), mDownloadState);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (mReferrer) {
- nsAutoCString referrer;
- rv = mReferrer->GetSpec(referrer);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("referrer"), referrer);
- } else {
- rv = stmt->BindNullByName(NS_LITERAL_CSTRING("referrer"));
- }
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("entityID"), mEntityID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- int64_t currBytes;
- (void)GetAmountTransferred(&currBytes);
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("currBytes"), currBytes);
- NS_ENSURE_SUCCESS(rv, rv);
-
- int64_t maxBytes;
- (void)GetSize(&maxBytes);
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("maxBytes"), maxBytes);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), mAutoResume);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mID);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return stmt->Execute();
-}
-
-nsresult
-nsDownload::FailDownload(nsresult aStatus, const char16_t *aMessage)
-{
- // Grab the bundle before potentially losing our member variables
- nsCOMPtr<nsIStringBundle> bundle = mDownloadManager->mBundle;
-
- (void)SetState(nsIDownloadManager::DOWNLOAD_FAILED);
-
- // Get title for alert.
- nsXPIDLString title;
- nsresult rv = bundle->GetStringFromName(
- u"downloadErrorAlertTitle", getter_Copies(title));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Get a generic message if we weren't supplied one
- nsXPIDLString message;
- message = aMessage;
- if (message.IsEmpty()) {
- rv = bundle->GetStringFromName(
- u"downloadErrorGeneric", getter_Copies(message));
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- // Get Download Manager window to be parent of alert
- nsCOMPtr<nsIWindowMediator> wm =
- do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<mozIDOMWindowProxy> dmWindow;
- rv = wm->GetMostRecentWindow(u"Download:Manager",
- getter_AddRefs(dmWindow));
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Show alert
- nsCOMPtr<nsIPromptService> prompter =
- do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- return prompter->Alert(dmWindow, title, message);
-}