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
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.
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)
79 #ifdef DOWNLOAD_SCANNER
80 #include "nsDownloadScanner.h"
85 #include <CoreFoundation/CoreFoundation.h>
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
113 , nsINavHistoryObserver
117 nsDownloadManager
*nsDownloadManager::gDownloadManagerService
= nsnull
;
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
145 gDownloadManagerService
= nsnull
;
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
159 // First cancel the download so it's no longer active
160 rv
= CancelDownload(dl
->mID
);
163 if (NS_SUCCEEDED(rv
))
164 rv
= RetryDownload(dl
->mID
);
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();
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
);
220 nsDownloadManager::RemoveAllDownloads()
223 for (PRInt32 i
= mCurrentDownloads
.Count() - 1; i
>= 0; --i
) {
224 nsRefPtr
<nsDownload
> dl
= mCurrentDownloads
[0];
227 if (dl
->IsPaused() && GetQuitBehavior() != QUIT_AND_CANCEL
)
228 result
= mCurrentDownloads
.RemoveObject(dl
);
230 result
= CancelDownload(dl
->mID
);
232 // Track the failure, but don't miss out on other downloads
233 if (NS_FAILED(result
))
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
)) &&
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
]);
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
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();
317 nsDownloadManager::InitMemoryDB()
319 mDBConn
= GetMemoryDBConnection();
321 return NS_ERROR_NOT_AVAILABLE
;
323 nsresult rv
= CreateTable();
324 NS_ENSURE_SUCCESS(rv
, rv
);
326 mDBType
= DATABASE_MEMORY
;
331 nsDownloadManager::InitFileDB()
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
);
346 rv
= mDBConn
->TableExists(NS_LITERAL_CSTRING("moz_downloads"), &tableExists
);
347 NS_ENSURE_SUCCESS(rv
, rv
);
350 NS_ENSURE_SUCCESS(rv
, rv
);
351 mDBType
= DATABASE_DISK
;
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
) {
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, "
383 "startTime INTEGER, "
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, "
408 "startTime INTEGER, "
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
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
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
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
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
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
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
527 rv
= mDBConn
->SetSchemaVersion(schemaVersion
);
528 NS_ENSURE_SUCCESS(rv
, rv
);
530 // Fallthrough to the next upgrade
532 // Extra sanity checking for developers
534 case DM_SCHEMA_VERSION
:
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
546 rv
= mDBConn
->SetSchemaVersion(DM_SCHEMA_VERSION
);
547 NS_ENSURE_SUCCESS(rv
, rv
);
549 // Fallthrough to downgrade check
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.
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
))
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
);
579 rv
= mDBConn
->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
580 "DROP TABLE moz_downloads"));
581 NS_ENSURE_SUCCESS(rv
, rv
);
584 NS_ENSURE_SUCCESS(rv
, rv
);
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, "
605 "startTime INTEGER, "
610 "currBytes INTEGER NOT NULL DEFAULT 0, "
611 "maxBytes INTEGER NOT NULL DEFAULT -1, "
613 "preferredApplication TEXT, "
614 "preferredAction INTEGER NOT NULL DEFAULT 0, "
615 "autoResume INTEGER NOT NULL DEFAULT 0"
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 "
628 "WHERE state = ?2"), getter_AddRefs(stmt
));
629 NS_ENSURE_SUCCESS(rv
, rv
);
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 "
646 "OR state = ?4"), getter_AddRefs(stmt
));
647 NS_ENSURE_SUCCESS(rv
, rv
);
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 "
668 "AND autoResume = ?3"),
669 getter_AddRefs(stmt
));
670 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
687 nsDownloadManager::RestoreActiveDownloads()
689 nsCOMPtr
<mozIStorageStatement
> stmt
;
690 nsresult rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
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
;
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
);
722 nsDownloadManager::AddDownloadToDB(const nsAString
&aName
,
723 const nsACString
&aSource
,
724 const nsACString
&aTarget
,
725 const nsAString
&aTempPath
,
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);
742 rv
= stmt
->BindStringParameter(i
++, aName
);
743 NS_ENSURE_SUCCESS(rv
, 0);
746 rv
= stmt
->BindUTF8StringParameter(i
++, aSource
);
747 NS_ENSURE_SUCCESS(rv
, 0);
750 rv
= stmt
->BindUTF8StringParameter(i
++, aTarget
);
751 NS_ENSURE_SUCCESS(rv
, 0);
754 rv
= stmt
->BindStringParameter(i
++, aTempPath
);
755 NS_ENSURE_SUCCESS(rv
, 0);
758 rv
= stmt
->BindInt64Parameter(i
++, aStartTime
);
759 NS_ENSURE_SUCCESS(rv
, 0);
762 rv
= stmt
->BindInt64Parameter(i
++, aEndTime
);
763 NS_ENSURE_SUCCESS(rv
, 0);
766 rv
= stmt
->BindInt32Parameter(i
++, nsIDownloadManager::DOWNLOAD_NOTSTARTED
);
767 NS_ENSURE_SUCCESS(rv
, 0);
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);
778 rv
= stmt
->BindInt32Parameter(i
++, aPreferredAction
);
779 NS_ENSURE_SUCCESS(rv
, 0);
782 rv
= stmt
->ExecuteStep(&hasMore
); // we want to keep our lock
783 NS_ENSURE_SUCCESS(rv
, 0);
786 rv
= mDBConn
->GetLastInsertRowID(&id
);
787 NS_ENSURE_SUCCESS(rv
, 0);
789 // lock on DB from statement will be released once we return
794 nsDownloadManager::InitDB()
799 case DATABASE_MEMORY
:
808 NS_ERROR("Unexpected value encountered for nsDownloadManager::mDBType");
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, "
818 "WHERE id = ?10"), getter_AddRefs(mUpdateDownloadStatement
));
819 NS_ENSURE_SUCCESS(rv
, rv
);
821 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
823 "FROM moz_downloads "
824 "WHERE source = ?1"), getter_AddRefs(mGetIdsForURIStatement
));
825 NS_ENSURE_SUCCESS(rv
, rv
);
831 nsDownloadManager::Init()
833 // Clean up any old downloads.rdf files from before Firefox 3
835 nsCOMPtr
<nsIFile
> oldDownloadsFile
;
837 if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE
,
838 getter_AddRefs(oldDownloadsFile
))) &&
839 NS_SUCCEEDED(oldDownloadsFile
->Exists(&fileExists
)) &&
841 (void)oldDownloadsFile
->Remove(PR_FALSE
);
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
);
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();
863 return NS_ERROR_OUT_OF_MEMORY
;
864 rv
= mScanner
->Init();
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
);
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
);
909 (void)history
->AddObserver(this, PR_FALSE
);
915 nsDownloadManager::GetRetentionBehavior()
917 // We use 0 as the default, which is "remove when done"
919 nsCOMPtr
<nsIPrefBranch
> pref
= do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
);
920 NS_ENSURE_SUCCESS(rv
, 0);
923 rv
= pref
->GetIntPref(PREF_BDM_RETENTION
, &val
);
924 NS_ENSURE_SUCCESS(rv
, 0);
929 enum nsDownloadManager::QuitBehavior
930 nsDownloadManager::GetQuitBehavior()
932 // We use 0 as the default, which is "remember and resume the download"
934 nsCOMPtr
<nsIPrefBranch
> pref
= do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
);
935 NS_ENSURE_SUCCESS(rv
, QUIT_AND_RESUME
);
938 rv
= pref
->GetIntPref(PREF_BDM_QUITBEHAVIOR
, &val
);
939 NS_ENSURE_SUCCESS(rv
, QUIT_AND_RESUME
);
943 return QUIT_AND_PAUSE
;
945 return QUIT_AND_CANCEL
;
947 return QUIT_AND_RESUME
;
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();
978 return NS_ERROR_OUT_OF_MEMORY
;
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
++);
988 stmt
->GetUTF8String(i
++, source
);
989 rv
= NS_NewURI(getter_AddRefs(dl
->mSource
), source
);
990 NS_ENSURE_SUCCESS(rv
, rv
);
993 stmt
->GetUTF8String(i
++, target
);
994 rv
= NS_NewURI(getter_AddRefs(dl
->mTarget
), target
);
995 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
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
);
1062 // Compensate for the i++s skipped in the true block
1067 static_cast<enum nsDownload::AutoResume
>(stmt
->AsInt32(i
++));
1069 // Addrefing and returning
1070 NS_ADDREF(*retVal
= dl
);
1075 nsDownloadManager::AddToCurrentDownloads(nsDownload
*aDl
)
1077 if (!mCurrentDownloads
.AppendObject(aDl
))
1078 return NS_ERROR_OUT_OF_MEMORY
;
1080 aDl
->mDownloadManager
= this;
1085 nsDownloadManager::SendEvent(nsDownload
*aDownload
, const char *aTopic
)
1087 (void)mObserverService
->NotifyObservers(aDownload
, aTopic
, nsnull
);
1090 ////////////////////////////////////////////////////////////////////////////////
1091 //// nsIDownloadManager
1094 nsDownloadManager::GetActiveDownloadCount(PRInt32
*aResult
)
1096 *aResult
= mCurrentDownloads
.Count();
1102 nsDownloadManager::GetActiveDownloads(nsISimpleEnumerator
**aResult
)
1104 return NS_NewArrayEnumerator(aResult
, mCurrentDownloads
);
1108 nsDownloadManager::GetDefaultDownloadsDirectory(nsILocalFile
**aResult
)
1110 nsCOMPtr
<nsILocalFile
> downloadDir
;
1113 nsCOMPtr
<nsIProperties
> dirService
=
1114 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
, &rv
);
1115 NS_ENSURE_SUCCESS(rv
, rv
);
1120 // User download directory
1124 // My Documents/Downloads
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
);
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.
1165 rv
= downloadDir
->Exists(&exists
);
1166 NS_ENSURE_SUCCESS(rv
, rv
);
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
1179 rv
= dirService
->Get(NS_UNIX_XDG_DOCUMENTS_DIR
,
1180 NS_GET_IID(nsILocalFile
),
1181 getter_AddRefs(downloadDir
));
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
);
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
);
1205 downloadDir
.forget(aResult
);
1210 #define NS_BRANCH_DOWNLOAD "browser.download."
1211 #define NS_PREF_FOLDERLIST "folderList"
1212 #define NS_PREF_DIR "dir"
1215 nsDownloadManager::GetUserDownloadsDirectory(nsILocalFile
**aResult
)
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
);
1232 rv
= prefBranch
->GetIntPref(NS_PREF_FOLDERLIST
,
1234 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
1251 case 1: // Downloads
1252 return GetDefaultDownloadsDirectory(aResult
);
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
);
1264 rv
= customDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0755);
1265 if (NS_SUCCEEDED(rv
)) {
1266 customDirectory
.forget(aResult
);
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
);
1284 rv
= GetDefaultDownloadsDirectory(aResult
);
1285 if (NS_SUCCEEDED(rv
)) {
1286 (void)prefBranch
->SetComplexValue(NS_PREF_DIR
,
1287 NS_GET_IID(nsILocalFile
),
1294 return NS_ERROR_INVALID_ARG
;
1298 nsDownloadManager::AddDownload(DownloadType aDownloadType
,
1301 const nsAString
& aDisplayName
,
1302 nsIMIMEInfo
*aMIMEInfo
,
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
);
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();
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
;
1342 nsCAutoString source
, target
;
1343 aSource
->GetSpec(source
);
1344 aTarget
->GetSpec(target
);
1346 // Track the temp file for exthandler downloads
1347 nsAutoString tempPath
;
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
;
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
);
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
);
1382 rv
= AddToCurrentDownloads(dl
);
1383 (void)dl
->SetState(startState
);
1384 NS_ENSURE_SUCCESS(rv
, rv
);
1386 #ifdef DOWNLOAD_SCANNER
1388 PRBool scan
= PR_TRUE
;
1389 nsCOMPtr
<nsIPrefBranch
> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID
));
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)
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
;
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
);
1417 PRBool enabled
= PR_FALSE
;
1418 (void)pc
->GetBlockFileDownloadsEnabled(&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
);
1428 (void)pc
->Log(nsIParentalControlsService::ePCLog_FileDownload
,
1435 NS_ADDREF(*aDownload
= dl
);
1441 nsDownloadManager::GetDownload(PRUint32 aID
, nsIDownload
**aDownloadItem
)
1443 nsDownload
*itm
= FindDownload(aID
);
1445 nsRefPtr
<nsDownload
> dl
;
1447 nsresult rv
= GetDownloadFromDB(aID
, getter_AddRefs(dl
));
1448 NS_ENSURE_SUCCESS(rv
, rv
);
1453 NS_ADDREF(*aDownloadItem
= itm
);
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
];
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.
1480 return NS_ERROR_FAILURE
;
1482 // Don't cancel if download is already finished
1483 if (dl
->IsFinished())
1486 // if the download is fake-paused, we have to resume it so we can cancel it
1487 if (dl
->IsPaused() && !dl
->IsResumable())
1490 // Have the download cancel its connection
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
1497 if (dl
->mTempFile
) {
1499 dl
->mTempFile
->Exists(&exists
);
1501 dl
->mTempFile
->Remove(PR_FALSE
);
1504 nsresult rv
= dl
->SetState(nsIDownloadManager::DOWNLOAD_CANCELED
);
1505 NS_ENSURE_SUCCESS(rv
, rv
);
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()) {
1528 if (NS_SUCCEEDED(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
);
1565 nsDownloadManager::RemoveDownload(PRUint32 aID
)
1567 nsDownload
*dl
= FindDownload(aID
);
1568 NS_ASSERTION(!dl
, "Can't call RemoveDownload on a download in progress!");
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",
1597 nsDownloadManager::RemoveDownloadsByTimeframe(PRInt64 aStartTime
,
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
);
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
);
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",
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 "
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",
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(
1681 "FROM moz_downloads "
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
);
1699 rv
= stmt
->GetInt32(0, &count
);
1708 nsDownloadManager::PauseDownload(PRUint32 aID
)
1710 nsDownload
*dl
= FindDownload(aID
);
1712 return NS_ERROR_FAILURE
;
1718 nsDownloadManager::ResumeDownload(PRUint32 aID
)
1720 nsDownload
*dl
= FindDownload(aID
);
1722 return NS_ERROR_FAILURE
;
1724 return dl
->Resume();
1728 nsDownloadManager::GetDBConnection(mozIStorageConnection
**aDBConn
)
1730 NS_ADDREF(*aDBConn
= mDBConn
);
1736 nsDownloadManager::AddListener(nsIDownloadProgressListener
*aListener
)
1738 mListeners
.AppendObject(aListener
);
1744 nsDownloadManager::RemoveListener(nsIDownloadProgressListener
*aListener
)
1746 mListeners
.RemoveObject(aListener
);
1752 nsDownloadManager::NotifyListenersOnDownloadStateChange(PRInt16 aOldState
,
1753 nsIDownload
*aDownload
)
1755 for (PRInt32 i
= mListeners
.Count() - 1; i
>= 0; --i
)
1756 mListeners
[i
]->OnDownloadStateChange(aOldState
, aDownload
);
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
);
1775 nsDownloadManager::NotifyListenersOnStateChange(nsIWebProgress
*aProgress
,
1776 nsIRequest
*aRequest
,
1777 PRUint32 aStateFlags
,
1779 nsIDownload
*aDownload
)
1781 for (PRInt32 i
= mListeners
.Count() - 1; i
>= 0; --i
)
1782 mListeners
[i
]->OnStateChange(aProgress
, aRequest
, aStateFlags
, aStatus
,
1787 nsDownloadManager::SwitchDatabaseTypeTo(enum nsDownloadManager::DatabaseType aType
)
1789 if (aType
== mDBType
)
1790 return NS_OK
; // no-op
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");
1811 ////////////////////////////////////////////////////////////////////////////////
1812 //// nsINavHistoryObserver
1815 nsDownloadManager::OnBeginUpdateBatch()
1817 // We already have a transaction, so don't make another
1818 if (mHistoryTransaction
)
1821 // Start a transaction that commits when deleted
1822 mHistoryTransaction
= new mozStorageTransaction(mDBConn
, PR_TRUE
);
1828 nsDownloadManager::OnEndUpdateBatch()
1830 // Get rid of the transaction and cause it to commit
1831 mHistoryTransaction
= nsnull
;
1837 nsDownloadManager::OnVisit(nsIURI
*aURI
, PRInt64 aVisitID
, PRTime aTime
,
1838 PRInt64 aSessionID
, PRInt64 aReferringID
,
1839 PRUint32 aTransitionType
, PRUint32
*aAdded
)
1845 nsDownloadManager::OnTitleChanged(nsIURI
*aURI
, const nsAString
&aPageTitle
)
1851 nsDownloadManager::OnBeforeDeleteURI(nsIURI
*aURI
)
1857 nsDownloadManager::OnDeleteURI(nsIURI
*aURI
)
1859 return RemoveDownloadsForURI(aURI
);
1863 nsDownloadManager::OnClearHistory()
1869 nsDownloadManager::OnPageChanged(nsIURI
*aURI
, PRUint32 aWhat
,
1870 const nsAString
&aValue
)
1876 nsDownloadManager::OnDeleteVisits(nsIURI
*aURI
, PRTime aVisitTime
)
1878 // Don't bother removing downloads until the page is removed.
1882 ////////////////////////////////////////////////////////////////////////////////
1886 nsDownloadManager::Observe(nsISupports
*aSubject
,
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
--;
1900 if (strcmp(aTopic
, "oncancel") == 0) {
1901 nsCOMPtr
<nsIDownload
> dl
= do_QueryInterface(aSubject
, &rv
);
1902 NS_ENSURE_SUCCESS(rv
, rv
);
1906 nsDownload
*dl2
= FindDownload(id
);
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)
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
);
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());
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());
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
);
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();
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
;
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
;
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
)
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() };
2067 mBundle
->FormatStringFromName(aCancelMessageMultiple
, strings
, 1,
2068 getter_Copies(message
));
2069 mBundle
->FormatStringFromName(NS_LITERAL_STRING("cancelDownloadsOKTextMultiple").get(),
2070 strings
, 1, getter_Copies(quitButton
));
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
;
2083 wm
->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(),
2084 getter_AddRefs(dmWindow
));
2088 nsCOMPtr
<nsIPromptService
> prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID
));
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
;
2093 prompter
->ConfirmEx(dmWindow
, title
, message
, flags
, quitButton
.get(), dontQuitButton
.get(), nsnull
, nsnull
, ¬hing
, &button
);
2095 aCancelDownloads
->SetData(button
== 1);
2099 ////////////////////////////////////////////////////////////////////////////////
2102 NS_IMPL_ISUPPORTS4(nsDownload
, nsIDownload
, nsITransfer
, nsIWebProgressListener
,
2103 nsIWebProgressListener2
)
2105 nsDownload::nsDownload() : mDownloadState(nsIDownloadManager::DOWNLOAD_NOTSTARTED
),
2107 mPercentComplete(0),
2111 mLastUpdate(PR_Now() - (PRUint32
)gUpdateInterval
),
2114 mHasMultipleFiles(PR_FALSE
),
2115 mAutoResume(DONT_RESUME
)
2119 nsDownload::~nsDownload()
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.
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
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
))
2157 mDownloadState
= aState
= nsIDownloadManager::DOWNLOAD_FINISHED
;
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
);
2171 // Now that we're done with handling the download, clean it up
2174 nsCOMPtr
<nsIPrefBranch
> pref(do_GetService(NS_PREFSERVICE_CONTRACTID
));
2176 // Master pref to control this function.
2177 PRBool showTaskbarAlert
= PR_TRUE
;
2179 pref
->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE
, &showTaskbarAlert
);
2181 if (showTaskbarAlert
) {
2182 PRInt32 alertInterval
= 2000;
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");
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
,
2219 nsCOMPtr
<nsIFileURL
> fileURL
= do_QueryInterface(mTarget
);
2221 nsCOMPtr
<nsIFile
> file
;
2222 if (NS_SUCCEEDED(fileURL
->GetFile(getter_AddRefs(file
))) && file
) {
2224 #if (defined(XP_WIN) && !defined(WINCE)) || defined(XP_MACOSX)
2226 if (NS_SUCCEEDED(file
->GetPath(path
))) {
2229 // On windows, add the download to the system's "recent documents"
2230 // list, with a pref to disable.
2231 PRBool addToRecentDocs
= PR_TRUE
;
2233 pref
->GetBoolPref(PREF_BDM_ADDTORECENTDOCS
, &addToRecentDocs
);
2235 if (addToRecentDocs
&&
2236 !nsDownloadManager::gDownloadManagerService
->mInPrivateBrowsing
) {
2237 ::SHAddToRecentDocs(SHARD_PATHW
, path
.get());
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
);
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
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
;
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
);
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
);
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");
2298 case nsIDownloadManager::DOWNLOAD_FAILED
:
2299 mDownloadManager
->SendEvent(this, "dl-failed");
2301 case nsIDownloadManager::DOWNLOAD_SCANNING
:
2302 mDownloadManager
->SendEvent(this, "dl-scanning");
2304 case nsIDownloadManager::DOWNLOAD_FINISHED
:
2305 mDownloadManager
->SendEvent(this, "dl-done");
2307 case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL
:
2308 case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY
:
2309 mDownloadManager
->SendEvent(this, "dl-blocked");
2311 case nsIDownloadManager::DOWNLOAD_DIRTY
:
2312 mDownloadManager
->SendEvent(this, "dl-dirty");
2314 case nsIDownloadManager::DOWNLOAD_CANCELED
:
2315 mDownloadManager
->SendEvent(this, "dl-cancel");
2323 ////////////////////////////////////////////////////////////////////////////////
2324 //// nsIWebProgressListener2
2327 nsDownload::OnProgressChange64(nsIWebProgress
*aWebProgress
,
2328 nsIRequest
*aRequest
,
2329 PRInt64 aCurSelfProgress
,
2330 PRInt64 aMaxSelfProgress
,
2331 PRInt64 aCurTotalProgress
,
2332 PRInt64 aMaxTotalProgress
)
2335 mRequest
= aRequest
; // used for pause/resume/last modification time
2337 if (mDownloadState
== nsIDownloadManager::DOWNLOAD_QUEUED
) {
2338 // Obtain the referrer
2340 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
2341 nsCOMPtr
<nsIURI
> referrer
= mReferrer
;
2343 (void)NS_GetReferrerFromChannel(channel
, getter_AddRefs(mReferrer
));
2345 // Restore the original referrer if the new one isn't useful
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.
2352 nsCOMPtr
<nsIDownloadHistory
> dh
=
2353 do_GetService(NS_DOWNLOADHISTORY_CONTRACTID
);
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
)
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) {
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
;
2406 nsDownload::OnRefreshAttempted(nsIWebProgress
*aWebProgress
,
2410 PRBool
*allowRefresh
)
2412 *allowRefresh
= PR_TRUE
;
2416 ////////////////////////////////////////////////////////////////////////////////
2417 //// nsIWebProgressListener
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
);
2433 nsDownload::OnLocationChange(nsIWebProgress
*aWebProgress
,
2434 nsIRequest
*aRequest
, nsIURI
*aLocation
)
2440 nsDownload::OnStatusChange(nsIWebProgress
*aWebProgress
,
2441 nsIRequest
*aRequest
, nsresult aStatus
,
2442 const PRUnichar
*aMessage
)
2444 if (NS_FAILED(aStatus
))
2445 return FailDownload(aStatus
, aMessage
);
2450 nsDownload::OnStateChange(nsIWebProgress
*aWebProgress
,
2451 nsIRequest
*aRequest
, PRUint32 aStateFlags
,
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
)) {
2461 nsCOMPtr
<nsIHttpChannel
> channel
= do_QueryInterface(aRequest
, &rv
);
2462 if (NS_SUCCEEDED(rv
)) {
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
2470 // Fail the download
2471 (void)SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL
);
2474 } else if ((aStateFlags
& STATE_STOP
) && (aStateFlags
& STATE_IS_NETWORK
) &&
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.
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
2496 } else if (mMaxBytes
== -1) {
2497 mMaxBytes
= mCurrBytes
;
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
));
2509 (void)prefs
->GetBoolPref(PREF_BDM_SCANWHENDONE
, &scan
);
2512 (void)SetState(nsIDownloadManager::DOWNLOAD_SCANNING
);
2514 (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED
);
2516 (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED
);
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);
2530 nsDownload::OnSecurityChange(nsIWebProgress
*aWebProgress
,
2531 nsIRequest
*aRequest
, PRUint32 aState
)
2536 ////////////////////////////////////////////////////////////////////////////////
2540 nsDownload::Init(nsIURI
*aSource
,
2542 const nsAString
& aDisplayName
,
2543 nsIMIMEInfo
*aMIMEInfo
,
2545 nsILocalFile
*aTempFile
,
2546 nsICancelable
*aCancelable
)
2548 NS_WARNING("Huh...how did we get here?!");
2553 nsDownload::GetState(PRInt16
*aState
)
2555 *aState
= mDownloadState
;
2560 nsDownload::GetDisplayName(nsAString
&aDisplayName
)
2562 aDisplayName
= mDisplayName
;
2567 nsDownload::GetCancelable(nsICancelable
**aCancelable
)
2569 *aCancelable
= mCancelable
;
2570 NS_IF_ADDREF(*aCancelable
);
2575 nsDownload::GetTarget(nsIURI
**aTarget
)
2578 NS_IF_ADDREF(*aTarget
);
2583 nsDownload::GetSource(nsIURI
**aSource
)
2586 NS_IF_ADDREF(*aSource
);
2591 nsDownload::GetStartTime(PRInt64
*aStartTime
)
2593 *aStartTime
= mStartTime
;
2598 nsDownload::GetPercentComplete(PRInt32
*aPercentComplete
)
2600 *aPercentComplete
= mPercentComplete
;
2605 nsDownload::GetAmountTransferred(PRInt64
*aAmountTransferred
)
2607 *aAmountTransferred
= mCurrBytes
+ (WasResumed() ? mResumedAt
: 0);
2612 nsDownload::GetSize(PRInt64
*aSize
)
2614 *aSize
= mMaxBytes
+ (WasResumed() && mMaxBytes
!= -1 ? mResumedAt
: 0);
2619 nsDownload::GetMIMEInfo(nsIMIMEInfo
**aMIMEInfo
)
2621 *aMIMEInfo
= mMIMEInfo
;
2622 NS_IF_ADDREF(*aMIMEInfo
);
2627 nsDownload::GetTargetFile(nsILocalFile
**aTargetFile
)
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
);
2642 nsDownload::GetSpeed(double *aSpeed
)
2649 nsDownload::GetId(PRUint32
*aId
)
2656 nsDownload::GetReferrer(nsIURI
**referrer
)
2658 NS_IF_ADDREF(*referrer
= mReferrer
);
2663 nsDownload::GetResumable(PRBool
*resumable
)
2665 *resumable
= IsResumable();
2669 ////////////////////////////////////////////////////////////////////////////////
2670 //// nsDownload Helper Functions
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();
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
;
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())
2697 // We need to bail if for some reason the temp file got removed
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
;
2705 nsresult rv
= mMIMEInfo
->GetPreferredAction(&action
);
2706 NS_ENSURE_SUCCESS(rv
, rv
);
2709 nsresult retVal
= NS_OK
;
2711 case nsIMIMEInfo::saveToDisk
:
2712 // Move the file to the proper location
2713 retVal
= MoveTempToTarget();
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();
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.
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
);
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
;
2791 deleteTempFileOnExit
= PR_FALSE
;
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.
2807 (void)appLauncher
->DeleteTemporaryFileOnExit(target
);
2814 nsDownload::SetStartTime(PRInt64 aStartTime
)
2816 mStartTime
= aStartTime
;
2817 mLastUpdate
= aStartTime
;
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;
2836 mPercentComplete
= (PRInt32
)((PRFloat64
)currBytes
/ maxBytes
* 100 + .5);
2843 return NS_ERROR_UNEXPECTED
;
2845 nsresult rv
= Cancel();
2846 NS_ENSURE_SUCCESS(rv
, rv
);
2848 return SetState(nsIDownloadManager::DOWNLOAD_PAUSED
);
2852 nsDownload::Cancel()
2854 nsresult rv
= NS_OK
;
2856 rv
= mCancelable
->Cancel(NS_BINDING_ABORTED
);
2857 // we're done with this, so break the cycle
2858 mCancelable
= nsnull
;
2865 nsDownload::Resume()
2867 if (!IsPaused() || !IsResumable())
2868 return NS_ERROR_UNEXPECTED
;
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.
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
)))
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
2912 SetProgressBytes(0, maxBytes
!= -1 ? maxBytes
- fileSize
: -1);
2913 // Track where we resumed because progress notifications restart at 0
2914 mResumedAt
= fileSize
;
2918 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
2920 rv
= httpChannel
->SetReferrer(mReferrer
);
2921 NS_ENSURE_SUCCESS(rv
, rv
);
2925 // Creates a cycle that will be broken when the download finishes
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
);
2937 return SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING
);
2941 nsDownload::IsPaused()
2943 return mDownloadState
== nsIDownloadManager::DOWNLOAD_PAUSED
;
2947 nsDownload::IsResumable()
2949 return !mEntityID
.IsEmpty();
2953 nsDownload::WasResumed()
2955 return mResumedAt
!= -1;
2959 nsDownload::ShouldAutoResume()
2961 return mAutoResume
== AUTO_RESUME
;
2965 nsDownload::IsFinishable()
2967 return mDownloadState
== nsIDownloadManager::DOWNLOAD_NOTSTARTED
||
2968 mDownloadState
== nsIDownloadManager::DOWNLOAD_QUEUED
||
2969 mDownloadState
== nsIDownloadManager::DOWNLOAD_DOWNLOADING
;
2973 nsDownload::IsFinished()
2975 return mDownloadState
== nsIDownloadManager::DOWNLOAD_FINISHED
;
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
;
2988 nsAutoString tempPath
;
2990 (void)mTempFile
->GetPath(tempPath
);
2991 nsresult rv
= stmt
->BindStringParameter(i
++, tempPath
);
2994 rv
= stmt
->BindInt64Parameter(i
++, mStartTime
);
2995 NS_ENSURE_SUCCESS(rv
, rv
);
2998 rv
= stmt
->BindInt64Parameter(i
++, mLastUpdate
);
2999 NS_ENSURE_SUCCESS(rv
, rv
);
3002 rv
= stmt
->BindInt32Parameter(i
++, mDownloadState
);
3003 NS_ENSURE_SUCCESS(rv
, rv
);
3007 nsCAutoString referrer
;
3008 rv
= mReferrer
->GetSpec(referrer
);
3009 NS_ENSURE_SUCCESS(rv
, rv
);
3010 rv
= stmt
->BindUTF8StringParameter(i
++, referrer
);
3012 rv
= stmt
->BindNullParameter(i
++);
3014 NS_ENSURE_SUCCESS(rv
, rv
);
3017 rv
= stmt
->BindUTF8StringParameter(i
++, mEntityID
);
3018 NS_ENSURE_SUCCESS(rv
, rv
);
3022 (void)GetAmountTransferred(&currBytes
);
3023 rv
= stmt
->BindInt64Parameter(i
++, currBytes
);
3024 NS_ENSURE_SUCCESS(rv
, rv
);
3028 (void)GetSize(&maxBytes
);
3029 rv
= stmt
->BindInt64Parameter(i
++, maxBytes
);
3030 NS_ENSURE_SUCCESS(rv
, rv
);
3033 rv
= stmt
->BindInt32Parameter(i
++, mAutoResume
);
3034 NS_ENSURE_SUCCESS(rv
, rv
);
3037 rv
= stmt
->BindInt64Parameter(i
++, mID
);
3038 NS_ENSURE_SUCCESS(rv
, rv
);
3040 return stmt
->Execute();
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
;
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
);
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
)
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
);
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
);
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
);
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
;