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